package org.oddjob.beanbus;
import java.util.Collection;
import org.apache.log4j.Logger;
public class BasicBeanBus<T> extends AbstractDestination<T>
implements BeanBus<T> {
private static final Logger logger = Logger.getLogger(BasicBeanBus.class);
private volatile Collection<? super T> to;
private boolean started = false;
private boolean tripping = false;
private final Runnable stopBusCommand;
private final AbstractBusConductor busConductor =
new AbstractBusConductor() {
@Override
public void requestBusStop() {
busConductor.fireBusStopRequested(started);
if (stopBusCommand != null) {
stopBusCommand.run();
}
}
@Override
public void cleanBus() throws BusCrashException {
// should it be an exception to clean a bus that hasn't had beans?
// No - because two components could both ask to clean the bus
// between trips.
if (tripping) {
tripEnd();
}
}
@Override
public String toString() {
return BusConductor.class.getSimpleName() + " for " +
BasicBeanBus.class.getSimpleName();
}
};
/**
* Constructor for an unstoppable bus.
*/
public BasicBeanBus() {
this(null);
}
/**
* Constructor for a stoppable bus.
*
* @param stopBusCommand
*/
public BasicBeanBus(Runnable stopBusCommand) {
this.stopBusCommand = stopBusCommand;
}
@Override
public void startBus() throws BusCrashException {
if (started) {
throw new IllegalStateException("Bus already started.");
}
try {
busConductor.fireBusStarting();
}
catch (BusCrashException e) {
busConductor.fireBusCrashed(BusPhase.BUS_STARTING, e);
busConductor.fireBusTerminated();
throw e;
}
started = true;
}
@Override
public void stopBus() throws BusCrashException {
if (!started) {
throw new IllegalStateException("Bus Not Started.");
}
if (tripping) {
tripEnd();
}
try {
busConductor.fireBusStopping();
}
finally {
terminateBus(BusPhase.BUS_STOPPED);
}
}
@Override
public boolean add(T bean) {
if (!started) {
throw new IllegalStateException("Bus Not Started.");
}
// if this is the first bean, start the trip.
if (!tripping) {
try {
tripBegin();
} catch (BusCrashException e) {
crashBus(BusPhase.TRIP_BEGINNING, e);
throw new RuntimeException(e);
}
// place here so we only log once.
if (to == null) {
logger.info("To is not set. All beans will be ignored.");
}
}
try {
if (to == null) {
return false;
}
else {
return to.add(bean);
}
}
catch (RuntimeException e) {
crashBus(BusPhase.BUS_RUNNING, e);
throw e;
}
}
private void tripBegin() throws BusCrashException {
try {
onTripBegin();
tripping = true;
busConductor.fireTripBeginning();
}
catch (BusCrashException e) {
crashBus(BusPhase.TRIP_BEGINNING, e);
throw e;
}
}
private void tripEnd() throws BusCrashException {
try {
busConductor.fireTripEnding();
tripping = false;
onTripEnd();
}
catch (BusCrashException e) {
crashBus(BusPhase.TRIP_ENDING, e);
throw e;
}
}
private void crashBus(BusPhase phase, Exception e) {
try {
busConductor.fireBusCrashed(phase, e);
}
finally {
terminateBus(BusPhase.BUS_CRASHED);
}
}
private void terminateBus(BusPhase phase) {
started = false;
busConductor.fireBusTerminated();
}
protected void onTripBegin() {
}
protected void onTripEnd() {
}
protected void onBusCrash() {
}
public Collection<? super T> getTo() {
return to;
}
public void setTo(Collection<? super T> to) {
this.to = to;
}
public Runnable getStopBusCommand() {
return stopBusCommand;
}
public BusConductor getBusConductor() {
return busConductor;
}
}