package org.prevayler.implementation;
import org.prevayler.Clock;
import org.prevayler.Query;
import org.prevayler.foundation.Cool;
import org.prevayler.foundation.DeepCopier;
import org.prevayler.foundation.serialization.Serializer;
import org.prevayler.implementation.publishing.TransactionPublisher;
import org.prevayler.implementation.publishing.TransactionSubscriber;
import java.io.IOException;
import java.util.Date;
public class PrevalentSystemGuard implements TransactionSubscriber {
private Object _prevalentSystem;
private long _systemVersion;
private boolean _ignoreRuntimeExceptions;
private final Serializer _journalSerializer;
public PrevalentSystemGuard( Object prevalentSystem, long systemVersion, Serializer journalSerializer){
_prevalentSystem=prevalentSystem;
_systemVersion=systemVersion;
_ignoreRuntimeExceptions=false;
_journalSerializer=journalSerializer;
}
public Object prevalentSystem(){
synchronized (this) {
if (_prevalentSystem == null) {
throw new Error("Prevayler is no longer allowing access to the prevalent system due to an Error thrown from an earlier transaction.");
}
return _prevalentSystem;
}
}
public void subscribeTo( TransactionPublisher publisher) throws IOException, ClassNotFoundException {
long initialTransaction;
synchronized (this) {
_ignoreRuntimeExceptions=true;
initialTransaction=_systemVersion + 1;
}
publisher.subscribe(this,initialTransaction);
synchronized (this) {
_ignoreRuntimeExceptions=false;
}
}
public void receive( TransactionTimestamp transactionTimestamp){
Capsule capsule=transactionTimestamp.capsule();
long systemVersion=transactionTimestamp.systemVersion();
Date executionTime=transactionTimestamp.executionTime();
synchronized (this) {
if (_prevalentSystem == null) {
throw new Error("Prevayler is no longer processing transactions due to an Error thrown from an earlier transaction.");
}
if (systemVersion != _systemVersion + 1) {
throw new IllegalStateException("Attempted to apply transaction " + systemVersion + " when prevalent system was only at "+ _systemVersion);
}
_systemVersion=systemVersion;
try {
capsule.executeOn(_prevalentSystem,executionTime,_journalSerializer);
}
catch ( RuntimeException rx) {
if (!_ignoreRuntimeExceptions) throw rx;
}
catch ( Error error) {
_prevalentSystem=null;
throw error;
}
finally {
notifyAll();
}
}
}
public Object executeQuery( Query sensitiveQuery, Clock clock) throws Exception {
synchronized (this) {
if (_prevalentSystem == null) {
throw new Error("Prevayler is no longer processing queries due to an Error thrown from an earlier transaction.");
}
synchronized (_prevalentSystem) {
return sensitiveQuery.query(_prevalentSystem,clock.time());
}
}
}
public PrevalentSystemGuard deepCopy( long systemVersion, Serializer snapshotSerializer) throws IOException, ClassNotFoundException {
synchronized (this) {
while (_systemVersion < systemVersion && _prevalentSystem != null) {
Cool.wait(this);
}
if (_prevalentSystem == null) {
throw new Error("Prevayler is no longer accepting transactions due to an Error thrown from an earlier transaction.");
}
if (_systemVersion > systemVersion) {
throw new IllegalStateException("Already at " + _systemVersion + "; can't go back to "+ systemVersion);
}
synchronized (_prevalentSystem) {
return new PrevalentSystemGuard(DeepCopier.deepCopyParallel(_prevalentSystem,snapshotSerializer),_systemVersion,_journalSerializer);
}
}
}
}