package org.oddjob.beanbus.mega;
import java.util.Collection;
import org.oddjob.Stateful;
import org.oddjob.arooa.deploy.annotations.ArooaComponent;
import org.oddjob.arooa.deploy.annotations.ArooaHidden;
import org.oddjob.arooa.deploy.annotations.ArooaInterceptor;
import org.oddjob.arooa.design.SerializableGenericDesignFactory;
import org.oddjob.arooa.parsing.ArooaContext;
import org.oddjob.arooa.parsing.ArooaElement;
import org.oddjob.arooa.parsing.ConfigurationOwner;
import org.oddjob.arooa.parsing.ConfigurationOwnerSupport;
import org.oddjob.arooa.parsing.ConfigurationSession;
import org.oddjob.arooa.parsing.ContextConfigurationSession;
import org.oddjob.arooa.parsing.OwnerStateListener;
import org.oddjob.arooa.parsing.SerializableDesignFactory;
import org.oddjob.beanbus.BeanBus;
import org.oddjob.beanbus.BusConductor;
import org.oddjob.beanbus.BusCrashException;
import org.oddjob.beanbus.BusEvent;
import org.oddjob.beanbus.BusService;
import org.oddjob.beanbus.BusServiceProvider;
import org.oddjob.beanbus.Outbound;
import org.oddjob.beanbus.SimpleBusService;
import org.oddjob.beanbus.TrackingBusListener;
import org.oddjob.framework.StructuralJob;
import org.oddjob.state.AnyActiveStateOp;
import org.oddjob.state.StateOperator;
/**
*
* @oddjob.description A job that allows the construction of a
* {@link BeanBus}.
* <p>
* A Bean Bus is an assembly of {@link Collection}s.
*
*
* @oddjob.example
*
* A simple bus example.
*
* {@oddjob.xml.resource org/oddjob/beanbus/mega/MegaBeanBusExample.xml}
*
*
* @author Rob Gordon
*
*/
@ArooaInterceptor("org.oddjob.beanbus.mega.MegaBeanBusInterceptor")
public class MegaBeanBus extends StructuralJob<Object>
implements ConfigurationOwner, BusServiceProvider {
private static final long serialVersionUID = 2012021500L;
/** Support for configuration modification. */
private transient volatile ConfigurationOwnerSupport configurationOwnerSupport;
private transient volatile BusConductor busConductor;
private volatile boolean noAutoLink;
private transient volatile ThreadLocal<BusPart> preparing;
private transient volatile boolean valid;
private transient volatile TrackingBusListener trackingBusListener;
/**
* Only constructor.
*/
public MegaBeanBus() {
completeConstruction();
}
private void completeConstruction() {
preparing = new ThreadLocal<BusPart>();
configurationOwnerSupport =
new ConfigurationOwnerSupport(this);
trackingBusListener = new TrackingBusListener() {
@Override
public void busStarting(BusEvent event) throws BusCrashException {
if (!valid) {
throw new BusCrashException(
"The Bus has changed. Run the whole bus again.");
}
}
};
}
@Override
@ArooaHidden
public void setArooaContext(ArooaContext context) {
super.setArooaContext(context);
configurationOwnerSupport.setConfigurationSession(
new ContextConfigurationSession(context));
}
/*
* (non-Javadoc)
* @see org.oddjob.arooa.parsing.ConfigurationOwner#provideConfigurationSession()
*/
@Override
public ConfigurationSession provideConfigurationSession() {
return configurationOwnerSupport.provideConfigurationSession();
}
/*
* (non-Javadoc)
* @see org.oddjob.arooa.parsing.ConfigurationOwner#addOwnerStateListener(org.oddjob.arooa.parsing.OwnerStateListener)
*/
@Override
public void addOwnerStateListener(OwnerStateListener listener) {
configurationOwnerSupport.addOwnerStateListener(listener);
}
/*
* (non-Javadoc)
* @see org.oddjob.arooa.parsing.ConfigurationOwner#removeOwnerStateListener(org.oddjob.arooa.parsing.OwnerStateListener)
*/
@Override
public void removeOwnerStateListener(OwnerStateListener listener) {
configurationOwnerSupport.removeOwnerStateListener(listener);
}
@Override
public SerializableDesignFactory rootDesignFactory() {
return new SerializableGenericDesignFactory(
this.getClass());
}
@Override
public ArooaElement rootElement() {
return null;
}
/*
* (non-Javadoc)
* @see org.oddjob.framework.StructuralJob#getStateOp()
*/
@Override
protected StateOperator getInitialStateOp() {
return new AnyActiveStateOp();
}
/**
* Add a child.
*
* @oddjob.property jobs
* @oddjob.description The child jobs.
* @oddjob.required No, but pointless if missing.
*
* @param child A child
*/
@ArooaComponent
public void setParts(int index, Object child) {
valid = false;
if (child == null) {
childHelper.removeChildAt(index);
}
else {
childHelper.insertChild(index, child);
}
}
/*
* (non-Javadoc)
* @see org.oddjob.jobs.AbstractJob#execute()
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
protected void execute() throws Exception {
Object[] children = childHelper.getChildren();
StatefulBusConductorAdapter adaptor = null;
Object previousChild = null;
try {
for (Object child : children) {
if (child instanceof BusServiceProvider) {
busConductor = ((BusServiceProvider) child).getServices(
).getService(SimpleBusService.BEAN_BUS_SERVICE_NAME);
}
if (busConductor == null && adaptor == null &&
child instanceof Stateful) {
adaptor = new StatefulBusConductorAdapter(
(Stateful) child);
busConductor = adaptor;
}
if (child instanceof BusPart) {
// We need to identify the bus conductor before the
// first bus part.
if (busConductor == null) {
throw new IllegalStateException("No Bus Conductor!");
}
BusPart busPart = (BusPart) child;
preparing.set(busPart);
try {
busPart.prepare(busConductor);
}
finally {
preparing.remove();
}
}
if (!noAutoLink && previousChild != null &&
child instanceof Collection) {
Outbound outbound = new OutboundStrategies(
).outboundFor(previousChild, getArooaSession());
if (outbound != null) {
outbound.setTo((Collection) child);
logger().info("Automatically Linked Outbound [" +
previousChild + "] to [" + child + "]");
}
}
previousChild = child;
}
valid = true;
trackingBusListener.setBusConductor(busConductor);
for (Object child : children) {
if (child instanceof Runnable) {
((Runnable) child).run();
}
}
}
finally {
if (adaptor != null) {
adaptor.close();
}
busConductor = null;
}
}
@Override
protected void onReset() {
super.onReset();
busConductor = null;
}
@Override
public BusService getServices() {
return new BusService() {
@Override
public String serviceNameFor(Class<?> theClass, String flavour) {
if (BusConductor.class == theClass) {
return BEAN_BUS_SERVICE_NAME;
}
else {
return null;
}
}
@Override
public BusConductor getService(String serviceName)
throws IllegalArgumentException {
BusConductor busConductor = MegaBeanBus.this.busConductor;
if (busConductor == null) {
throw new NullPointerException(
"Bus Service Not Available until the Bus is Running.");
}
BusPart busPart = preparing.get();
if (busPart != null) {
busConductor = busPart.conductorForService(busConductor);
}
return busConductor;
}
@Override
public String toString() {
BusConductor busConductor = MegaBeanBus.this.busConductor;
if (busConductor == null) {
return "No Bus Service Until Running.";
}
else {
return busConductor.toString();
}
}
};
}
public BusConductor getBusConductor() {
return busConductor;
}
public void setBusConductor(BusConductor busConductor) {
this.busConductor = busConductor;
}
public boolean isNoAutoLink() {
return noAutoLink;
}
public void setNoAutoLink(boolean noAutoLink) {
this.noAutoLink = noAutoLink;
}
}