//Prevayler(TM) - The Free-Software Prevalence Layer. //Copyright (C) 2001-2003 Klaus Wuestefeld //This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. package org.prevayler.implementation.publishing; import org.prevayler.Clock; import org.prevayler.foundation.Cool; import org.prevayler.foundation.Turn; import org.prevayler.implementation.Capsule; import org.prevayler.implementation.TransactionGuide; import org.prevayler.implementation.TransactionTimestamp; import org.prevayler.implementation.clock.PausableClock; import org.prevayler.implementation.journal.Journal; import org.prevayler.implementation.publishing.censorship.TransactionCensor; import java.io.IOException; public class CentralPublisher extends AbstractPublisher { private final PausableClock _pausableClock; private TransactionCensor _censor; private final Journal _journal; private volatile int _pendingPublications = 0; private final Object _pendingPublicationsMonitor = new Object(); private Turn _nextTurn = Turn.first(); private long _nextTransaction; private final Object _nextTurnMonitor = new Object(); public CentralPublisher(Clock clock, Journal journal) { super(new PausableClock(clock)); _pausableClock = (PausableClock) _clock; //This is just to avoid casting the inherited _clock every time. _journal = journal; } public CentralPublisher(Clock clock, TransactionCensor censor, Journal journal) { this(clock,journal); _censor = censor; } public void publish(Capsule capsule) { synchronized (_pendingPublicationsMonitor) { //Blocks all new subscriptions until the publication is over. if (_pendingPublications == 0) _pausableClock.pause(); _pendingPublications++; } try { publishWithoutWorryingAboutNewSubscriptions(capsule); // Suggestions for a better method name are welcome. :) } finally { synchronized (_pendingPublicationsMonitor) { _pendingPublications--; if (_pendingPublications == 0) { _pausableClock.resume(); _pendingPublicationsMonitor.notifyAll(); } } } } private void publishWithoutWorryingAboutNewSubscriptions(Capsule capsule) { TransactionGuide guide = approve(capsule); _journal.append(guide); notifySubscribers(guide); } private TransactionGuide approve(Capsule capsule) { synchronized (_nextTurnMonitor) { TransactionTimestamp timestamp = new TransactionTimestamp(capsule, _nextTransaction, _pausableClock.realTime()); _censor.approve(timestamp); // Only count this transaction once approved. Turn turn = _nextTurn; _nextTurn = _nextTurn.next(); _nextTransaction++; return new TransactionGuide(timestamp, turn); } } private void notifySubscribers(TransactionGuide guide) { guide.startTurn(); try { _pausableClock.advanceTo(guide.executionTime()); notifySubscribers(guide.timestamp()); } finally { guide.endTurn(); } } public void subscribe(TransactionSubscriber subscriber, long initialTransaction) throws IOException, ClassNotFoundException { synchronized (_pendingPublicationsMonitor) { while (_pendingPublications != 0) Cool.wait(_pendingPublicationsMonitor); _journal.update(subscriber, initialTransaction); synchronized (_nextTurnMonitor) { _nextTransaction = _journal.nextTransaction(); } super.addSubscriber(subscriber); } } public void close() throws IOException { _journal.close(); } }