ControlledFailoverMicroSpace.java
package org.microspace.space;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.microspace.failover.FailoverState;
import org.microspace.failover.FailoverStateListener;
import org.microspace.util.MicroLogger;
import org.microspace.util.UniqueId;
/**
* A failover microspace with local failover control.
*
* @author Gaspar Sinai {@literal gaspar.sinai@microspace.org}
* @version 2018-11-27
*/
public class ControlledFailoverMicroSpace extends MicroSpaceDelegator implements FailoverMicroSpace, ChainableFailoverMicroSpace
{
FailoverState failoverStateOverride;
FailoverState failoverStateDelegate;
final FailoverMicroSpace failoverSpaceDelegate;
final LocalFailoverStateListener listener;
protected final Lock failoverLock = new ReentrantLock ();
List<FailoverStateListener> listeners = new LinkedList<FailoverStateListener>();
MicroLogger log = new MicroLogger (ControlledFailoverMicroSpace.class);
public ControlledFailoverMicroSpace(FailoverMicroSpace space) {
this(space, null);
}
public ControlledFailoverMicroSpace(FailoverMicroSpace space, UniqueId spaceId) {
super(space, spaceId);
this.failoverStateOverride = FailoverState.INITIALIZING;
this.failoverSpaceDelegate = space;
this.listener = new LocalFailoverStateListener();
failoverSpaceDelegate.addFailoverStateListener(listener);
this.failoverStateDelegate = failoverSpaceDelegate.getFailoverState();
}
/**
* {@inheritDoc}
*/
public FailoverMicroSpace getBottomLevelFailoverMicroSpace() {
if (failoverSpaceDelegate instanceof ChainableFailoverMicroSpace) {
return ((ChainableFailoverMicroSpace) failoverSpaceDelegate).getBottomLevelFailoverMicroSpace();
}
return failoverSpaceDelegate;
}
@Override
public FailoverState getFailoverState() {
return getFailoverState(failoverStateDelegate, failoverStateOverride);
}
@Override
public void addFailoverStateListener(FailoverStateListener failoverStateListener) {
listeners.add(failoverStateListener);
}
@Override
public void removeFailoverStateListener(FailoverStateListener failoverStateListener) {
listeners.remove(failoverStateListener);
}
@Override
public void start() {
failoverSpaceDelegate.start();
}
@Override
public void shutdown() {
failoverSpaceDelegate.removeFailoverStateListener(listener);
failoverSpaceDelegate.shutdown();
}
private FailoverState getFailoverState(FailoverState underlyingState, FailoverState overrideState) {
switch (overrideState) {
case BACKUP:
if (underlyingState == FailoverState.INITIALIZING) {
return FailoverState.INITIALIZING;
}
return overrideState;
case INITIALIZING:
return overrideState;
case PRIMARY:
return underlyingState;
default:
break;
}
return null;
}
private void fireFailoverStateChangeBefore (FailoverState before, FailoverState after) {
for (FailoverStateListener l : listeners) {
try {
l.failoverStateChangeBefore(before, after);
} catch (Throwable e){
log.error("Failover Listener failed", e);
}
}
}
private void fireFailoverStateChangeAfter (FailoverState before, FailoverState after) {
for (FailoverStateListener l : listeners) {
try {
l.failoverStateChangeAfter(before, after);
} catch (Throwable e){
log.error("Failover Listener failed", e);
}
}
}
public void changeFailoveState (FailoverState newState) {
if (newState == FailoverState.INITIALIZING) {
throw new IllegalArgumentException("Can not change to INITIALIZING");
}
failoverLock.lock();
try {
FailoverState beforeOverride = getFailoverState(failoverStateDelegate, failoverStateOverride);
FailoverState afterOverride = getFailoverState(failoverStateDelegate, newState);
if (beforeOverride != afterOverride) {
fireFailoverStateChangeBefore(beforeOverride, afterOverride);
}
failoverStateOverride = newState;
if (beforeOverride != afterOverride) {
fireFailoverStateChangeAfter(beforeOverride, afterOverride);
}
} finally {
failoverLock.unlock();
}
}
private class LocalFailoverStateListener implements FailoverStateListener {
@Override
public void failoverStateChangeBefore(FailoverState before, FailoverState after) {
failoverLock.lock();
try {
FailoverState beforeOverride = getFailoverState(before, failoverStateOverride);
FailoverState afterOverride = getFailoverState(after, failoverStateOverride);
if (beforeOverride != afterOverride) {
fireFailoverStateChangeBefore(beforeOverride, afterOverride);
}
failoverStateDelegate = after;
} finally {
failoverLock.unlock();
}
}
@Override
public void failoverStateChangeAfter(FailoverState before, FailoverState after) {
failoverLock.lock();
try {
failoverStateDelegate = after;
FailoverState beforeOverride = getFailoverState(before, failoverStateOverride);
FailoverState afterOverride = getFailoverState(after, failoverStateOverride);
if (beforeOverride != afterOverride) {
fireFailoverStateChangeAfter(beforeOverride, afterOverride);
}
} finally {
failoverLock.unlock();
}
}
}
}