public void fire( DependentSubjectListener subject, Integer event, DependentSubjectListener listener) { // update the listener boolean incremented = listener.latestRevision < event.intValue(); listener.latestRevision = event.intValue(); // make sure the listener's dependencies were notified first listener.assertDependenciesSatisfiedRecursively(listener); // send the event forward if (incremented) { listener.increment(0); } }
/** * Dependencies are satisfied if the latestRevision is at least the latestRevision of all * upstream {@link DependentSubjectListener}s. */ public void assertDependenciesSatisfiedRecursively(DependentSubjectListener notified) { final Iterator<DependentSubjectListener> iter = upstreamSubjects.iterator(); while (iter.hasNext()) { final DependentSubjectListener upstream = iter.next(); upstream.assertDependenciesSatisfiedRecursively(notified); if (latestRevision < upstream.latestRevision) throw new IllegalStateException( "Dependencies not satisfied for " + notified + ", dependency " + this + " not updated by " + upstream + "!"); } }
/** * The publisher should throw an IllegalStateException when a cycle in the listener graph is * created. */ public void testCycleThrows() { SequenceDependenciesEventPublisher publisher = new SequenceDependenciesEventPublisher(); DependentSubjectListener a = new DependentSubjectListener("A", publisher); DependentSubjectListener b = new DependentSubjectListener("B", publisher); DependentSubjectListener c = new DependentSubjectListener("C", publisher); // simple cycle of three a.addListener(b); b.addListener(c); try { c.addListener(a); fail("Cycle not detected"); } catch (IllegalStateException e) { // expected } // cycle of 10 publisher = new SequenceDependenciesEventPublisher(); DependentSubjectListener[] subjects = new DependentSubjectListener[10]; for (int i = 0; i < 10; i++) { subjects[i] = new DependentSubjectListener("" + i, publisher); if (i > 0) subjects[i - 1].addListener(subjects[i]); } try { subjects[9].addListener(subjects[0]); fail("Cycle not detected"); } catch (IllegalStateException e) { // expected } // cycle of 1 publisher = new SequenceDependenciesEventPublisher(); a = new DependentSubjectListener("A", publisher); try { a.addListener(a); fail("Cycle not detected"); } catch (IllegalStateException e) { // expected } }
/** Register the listener as dependent on the subject. */ public void addListener(DependentSubjectListener listener) { downstreamListeners.add(listener); listener.upstreamSubjects.add(this); listener.latestRevision = Math.max(listener.latestRevision, latestRevision); this.publisher.addListener(this, listener, DependentSubjectListenerEventFormat.INSTANCE); }
/** * Prepare the original diamond-dependency problem, where B depends on A and C depends on both A * and B. We need to guarantee that C can read all of its dependencies only when they're in a * 'consistent' state, which is when they've received all events from their dependencies, and * their dependencies are in a consistent state. * * <p>A --> B --. This diagram shows A --> B, B --> C and A --> C. | | The only safe notification * order of events is | V for B to receive events from A before C receives '-------> C those same * events. */ public void testDiamondDependency() { SequenceDependenciesEventPublisher publisher = new SequenceDependenciesEventPublisher(); DependentSubjectListener a = new DependentSubjectListener("A", publisher); DependentSubjectListener b = new DependentSubjectListener("B", publisher); DependentSubjectListener c = new DependentSubjectListener("C", publisher); a.addListener(c); a.increment(10); assertEquals(10, a.latestRevision); assertEquals(0, b.latestRevision); assertEquals(10, c.latestRevision); c.increment(5); assertEquals(10, a.latestRevision); assertEquals(0, b.latestRevision); assertEquals(15, c.latestRevision); b.increment(20); assertEquals(10, a.latestRevision); assertEquals(20, b.latestRevision); assertEquals(15, c.latestRevision); b.addListener(c); assertEquals(10, a.latestRevision); assertEquals(20, b.latestRevision); assertEquals(20, c.latestRevision); b.increment(2); assertEquals(10, a.latestRevision); assertEquals(22, b.latestRevision); assertEquals(22, c.latestRevision); a.increment(15); assertEquals(25, a.latestRevision); assertEquals(22, b.latestRevision); assertEquals(25, c.latestRevision); a.addListener(b); assertEquals(25, a.latestRevision); assertEquals(25, b.latestRevision); assertEquals(25, c.latestRevision); a.increment(4); assertEquals(29, a.latestRevision); assertEquals(29, b.latestRevision); assertEquals(29, c.latestRevision); }