//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.replication; import org.prevayler.Clock; import org.prevayler.foundation.network.OldNetwork; import org.prevayler.foundation.network.ObjectSocket; import org.prevayler.implementation.Capsule; import org.prevayler.implementation.TransactionTimestamp; import org.prevayler.implementation.clock.BrokenClock; import org.prevayler.implementation.publishing.TransactionPublisher; import org.prevayler.implementation.publishing.TransactionSubscriber; import java.io.IOException; import java.util.Date; /** Reserved for future implementation. */ public class ClientPublisher implements TransactionPublisher { private final BrokenClock _clock = new BrokenClock(); private TransactionSubscriber _subscriber; private final Object _upToDateMonitor = new Object(); private Capsule _myCapsule; private final Object _myCapsuleMonitor = new Object(); private RuntimeException _myTransactionRuntimeException; private Error _myTransactionError; private final ObjectSocket _server; public ClientPublisher(OldNetwork network, String serverIpAddress, int serverPort) throws IOException { System.out.println("The replication logic is still under development."); _server = network.openSocket(serverIpAddress, serverPort); startListening(); } private void startListening() { Thread listener = new Thread() { public void run() { try { while (true) receiveTransactionFromServer(); } catch (Exception ex) { ex.printStackTrace(); } } }; listener.setDaemon(true); listener.start(); } public synchronized void subscribe(TransactionSubscriber subscriber, long initialTransaction) throws IOException, ClassNotFoundException { if (_subscriber != null) throw new UnsupportedOperationException("The current implementation can only support one subscriber. Future implementations will support more."); _subscriber = subscriber; synchronized (_upToDateMonitor) { _server.writeObject(new Long(initialTransaction)); wait(_upToDateMonitor); } } public void cancelSubscription(TransactionSubscriber subscriber) { throw new UnsupportedOperationException("Removing subscribers is not yet supported by the current implementation."); } //TODO Remove synchronized allowing multiple transactions to be sent at a time. public synchronized void publish(Capsule capsule) { if (_subscriber == null) throw new IllegalStateException("To publish a transaction, this ClientPublisher needs a registered subscriber."); synchronized (_myCapsuleMonitor) { _myCapsule = capsule; try { _server.writeObject(capsule); } catch (IOException iox) { iox.printStackTrace(); while (true) Thread.yield(); //Remove all exceptions when using StubbornNetwork. } wait(_myCapsuleMonitor); throwEventualErrors(); } } private void throwEventualErrors() throws RuntimeException, Error { try { if (_myTransactionRuntimeException != null) throw _myTransactionRuntimeException; if (_myTransactionError != null) throw _myTransactionError; } finally { _myTransactionRuntimeException = null; _myTransactionError = null; } } private void receiveTransactionFromServer() throws IOException, ClassNotFoundException { Object transactionCandidate = _server.readObject(); if (transactionCandidate.equals(ServerConnection.SUBSCRIBER_UP_TO_DATE)) { synchronized (_upToDateMonitor) { _upToDateMonitor.notify(); } return; } if (transactionCandidate instanceof Date) { Date clockTick = (Date)transactionCandidate; _clock.advanceTo(clockTick); return; } if (transactionCandidate instanceof RuntimeException) { _myTransactionRuntimeException = (RuntimeException)transactionCandidate; notifyMyTransactionMonitor(); return; } if (transactionCandidate instanceof Error) { _myTransactionError = (Error)transactionCandidate; notifyMyTransactionMonitor(); return; } TransactionTimestamp transactionTimestamp = (TransactionTimestamp)transactionCandidate; Date timestamp = transactionTimestamp.executionTime(); long systemVersion = transactionTimestamp.systemVersion(); _clock.advanceTo(timestamp); if (transactionTimestamp.capsule() == null) { _subscriber.receive(new TransactionTimestamp(_myCapsule, systemVersion, timestamp)); notifyMyTransactionMonitor(); return; } _subscriber.receive(new TransactionTimestamp(transactionTimestamp.capsule(), systemVersion, timestamp)); } private static void wait(Object monitor) { try { monitor.wait(); } catch (InterruptedException ix) { throw new RuntimeException("Unexpected InterruptedException."); } } private void notifyMyTransactionMonitor() { synchronized (_myCapsuleMonitor) { _myCapsuleMonitor.notify(); } } public Clock clock() { return _clock; } public void close() throws IOException { _server.close(); } }