package org.archstudio.myx.fw;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.CopyOnWriteArrayList;
import org.archstudio.myx.fw.IMyxLifecycleProcessor.Operation;
public class MyxBasicRuntime implements IMyxRuntime {
protected IMyxContainer mainContainer = new MyxContainer();
protected final List<BrickLoaderEntry> brickLoaders = Collections
.synchronizedList(new CopyOnWriteArrayList<BrickLoaderEntry>());
protected final Map<IMyxBrickDescription, IMyxBrickFactory> brickDescriptionToFactoryMap = Collections
.synchronizedMap(new HashMap<IMyxBrickDescription, IMyxBrickFactory>());
protected final List<IMyxWeld> weldList = Collections.synchronizedList(new CopyOnWriteArrayList<IMyxWeld>());
protected final MyxInterfaceRepository interfaceRepository = new MyxInterfaceRepository();
protected MyxBasicRuntime() {
}
protected static class BrickLoaderEntry {
private final IMyxName loaderName;
private final IMyxBrickLoader loader;
public BrickLoaderEntry(IMyxName loaderName, IMyxBrickLoader loader) {
this.loaderName = loaderName;
this.loader = loader;
}
public IMyxName getLoaderName() {
return loaderName;
}
public IMyxBrickLoader getLoader() {
return loader;
}
}
@Override
public void addBrickLoader(IMyxName loaderName, Class<? extends IMyxBrickLoader> brickLoaderClass,
Properties initParams) throws MyxBrickLoaderException {
try {
IMyxBrickLoader loader = null;
if (initParams == null) {
Constructor<?> constructor = brickLoaderClass.getConstructor(new Class[0]);
Object o = constructor.newInstance(new Object[0]);
loader = (IMyxBrickLoader) o;
}
else {
Constructor<?> constructor = brickLoaderClass.getConstructor(new Class<?>[] { Properties.class });
Object o = constructor.newInstance(new Object[] { initParams });
loader = (IMyxBrickLoader) o;
}
loader.setRuntime(this);
BrickLoaderEntry ble = new BrickLoaderEntry(loaderName, loader);
brickLoaders.add(ble);
}
catch (NoSuchMethodException nsme) {
throw new MyxBrickLoaderException(nsme);
}
catch (IllegalAccessException iae) {
throw new MyxBrickLoaderException(iae);
}
catch (InstantiationException ie) {
throw new MyxBrickLoaderException(ie);
}
catch (InvocationTargetException ite) {
throw new MyxBrickLoaderException(ite);
}
}
@Override
public void removeBrickLoader(IMyxName loaderName) {
synchronized (brickLoaders) {
for (BrickLoaderEntry ble : brickLoaders) {
if (loaderName.equals(ble.loaderName)) {
// This is OK because it's a CopyOnWriteArrayList
brickLoaders.remove(ble);
}
}
}
}
@Override
public Collection<? extends IMyxName> getBrickLoaderNames() {
synchronized (brickLoaders) {
List<IMyxName> nameList = new ArrayList<IMyxName>();
for (BrickLoaderEntry ble : brickLoaders) {
nameList.add(ble.loaderName);
}
return Collections.unmodifiableList(nameList);
}
}
/* Non-interface method */
public IMyxBrickLoader getBrickLoader(IMyxName name) {
synchronized (brickLoaders) {
for (BrickLoaderEntry ble : brickLoaders) {
if (ble.loaderName.equals(name)) {
return ble.loader;
}
}
return null;
}
}
/* Non-interface method */
public Collection<? extends IMyxBrickLoader> getAllBrickLoaders() {
synchronized (brickLoaders) {
List<IMyxBrickLoader> loaderList = new ArrayList<IMyxBrickLoader>();
for (BrickLoaderEntry ble : brickLoaders) {
loaderList.add(ble.loader);
}
return Collections.unmodifiableList(loaderList);
}
}
protected IMyxBrick createBrick(IMyxName brickName, IMyxBrickDescription brickDescription,
IMyxBrickInitializationData initializationData) throws MyxBrickLoadException, MyxBrickCreationException {
synchronized (brickDescriptionToFactoryMap) {
IMyxBrickFactory factory = brickDescriptionToFactoryMap.get(brickDescription);
if (factory == null) {
List<Exception> exceptionList = new ArrayList<Exception>();
for (IMyxBrickLoader brickLoader : getAllBrickLoaders()) {
try {
factory = brickLoader.load(brickDescription);
if (factory != null) {
break;
}
}
catch (MyxBrickNotFoundException bnfe) {
exceptionList.add(bnfe);
}
catch (MyxBrickLoadFailedException blfe) {
exceptionList.add(blfe);
}
catch (MyxUnsupportedBrickDescriptionException ubde) {
exceptionList.add(ubde);
}
}
if (factory == null) {
//we couldn't load the brick and create a factory
throw new MyxBrickLoadException(brickName.getName(), "Can't load brick with any loader",
exceptionList);
}
}
//Now we have a factory
IMyxBrick brick = factory.create(brickName, brickDescription, initializationData);
return brick;
}
}
@Override
public void addBrick(List<? extends IMyxName> path, IMyxName brickName, IMyxBrickDescription brickDescription,
IMyxBrickInitializationData initializationData) throws MyxBrickLoadException, MyxBrickCreationException {
IMyxContainer container = MyxUtils.resolvePath(mainContainer, path);
if (container == null) {
throw new MyxInvalidPathException(path);
}
IMyxBrick b = createBrick(brickName, brickDescription, initializationData);
//Let's set up the brick items
//First, the required service provider
IMyxRequiredServiceProvider requiredServiceProvider = new MyxBasicRequiredServiceProvider();
//Next, the interface manager
IMyxInterfaceManager interfaceManager = new MyxBasicInterfaceManager(this, path, brickName);
IMyxBrickItems brickItems = new MyxBasicBrickItems(brickName, requiredServiceProvider, interfaceManager,
brickDescription, initializationData);
b.setMyxBrickItems(brickItems);
container.addInternalBrick(b);
}
@Override
public void removeBrick(List<? extends IMyxName> path, IMyxName brickName) {
IMyxContainer container = MyxUtils.resolvePath(mainContainer, path);
if (container == null) {
throw new MyxInvalidPathException(path);
}
synchronized (container) {
IMyxBrick b = container.getInternalBrick(brickName);
if (b == null) {
throw new IllegalArgumentException("No brick found with name: " + brickName + " at "
+ MyxUtils.pathToString(path));
}
container.removeInternalBrick(b);
}
}
@Override
public Collection<? extends IMyxName> getAllBrickNames(List<? extends IMyxName> path) {
IMyxContainer container = MyxUtils.resolvePath(mainContainer, path);
if (container == null) {
throw new MyxInvalidPathException(path);
}
synchronized (container) {
List<IMyxName> names = new ArrayList<IMyxName>();
for (IMyxBrick internalBrick : container.getInternalBricks()) {
names.add(internalBrick.getMyxBrickItems().getBrickName());
}
return names;
}
}
@Override
public IMyxBrickDescription getBrickDescription(List<? extends IMyxName> path, IMyxName brickName) {
IMyxContainer container = MyxUtils.resolvePath(mainContainer, path);
if (container == null) {
throw new MyxInvalidPathException(path);
}
IMyxBrick b = container.getInternalBrick(brickName);
if (b == null) {
throw new IllegalArgumentException("No brick found with name: " + brickName + " at "
+ MyxUtils.pathToString(path));
}
return b.getMyxBrickItems().getBrickDescription();
}
@Override
public void addInterface(List<? extends IMyxName> path, IMyxName brickName, IMyxName interfaceName,
IMyxInterfaceDescription interfaceDescription, EMyxInterfaceDirection interfaceDirection) {
IMyxContainer container = MyxUtils.resolvePath(mainContainer, path);
if (container == null) {
throw new MyxInvalidPathException(path);
}
IMyxBrick b = container.getInternalBrick(brickName);
if (b == null) {
throw new IllegalArgumentException("No brick found with name: " + brickName + " at "
+ MyxUtils.pathToString(path));
}
//Don't use this method to add container interfaces, which are mapped.
if (b instanceof IMyxContainer) {
throw new IllegalArgumentException("Use addContainerInterface(...) to add interfaces to container "
+ brickName + " at " + MyxUtils.pathToString(path));
}
interfaceRepository.addInterface(b, interfaceName, interfaceDescription, interfaceDirection);
}
@Override
public void addContainerInterface(List<? extends IMyxName> path, IMyxName brickName, IMyxName interfaceName,
IMyxInterfaceDescription interfaceDescription, EMyxInterfaceDirection interfaceDirection,
IMyxName internalBrickName, IMyxName internalBrickInterfaceName) {
IMyxContainer outercontainer = MyxUtils.resolvePath(mainContainer, path);
if (outercontainer == null) {
throw new MyxInvalidPathException(path);
}
IMyxBrick b = outercontainer.getInternalBrick(brickName);
if (b == null) {
throw new IllegalArgumentException("No brick found with name: " + brickName + " at "
+ MyxUtils.pathToString(path));
}
if (!(b instanceof IMyxContainer)) {
throw new IllegalArgumentException("Brick " + brickName + " at " + MyxUtils.pathToString(path)
+ " should have been a container, but wasn't.");
}
IMyxContainer container = (IMyxContainer) b;
IMyxBrick internalBrick = container.getInternalBrick(internalBrickName);
if (internalBrick == null) {
throw new IllegalArgumentException("No brick found with name: " + internalBrickName + " at "
+ MyxUtils.pathToString(path) + "/" + brickName);
}
Collection<? extends IMyxName> internalInterfaceNames = interfaceRepository.getAllInterfaceNames(internalBrick);
if (!internalInterfaceNames.contains(internalBrickInterfaceName)) {
throw new IllegalArgumentException("No interface named: " + internalBrickInterfaceName + " on brick: "
+ internalBrickName + " at " + MyxUtils.pathToString(path) + "/" + brickName);
}
interfaceRepository.addInterface(b, interfaceName, interfaceDescription, interfaceDirection, internalBrickName,
internalBrickInterfaceName);
}
@Override
public void removeInterface(List<? extends IMyxName> path, IMyxName brickName, IMyxName interfaceName) {
IMyxContainer container = MyxUtils.resolvePath(mainContainer, path);
if (container == null) {
throw new MyxInvalidPathException(path);
}
IMyxBrick b = container.getInternalBrick(brickName);
if (b == null) {
throw new IllegalArgumentException("No brick found with name: " + brickName + " at "
+ MyxUtils.pathToString(path));
}
//TODO: Disallow the removal of interfaces that are involved in welds.
interfaceRepository.removeInterface(b, interfaceName);
}
@Override
public Collection<? extends IMyxName> getAllInterfaceNames(List<? extends IMyxName> path, IMyxName brickName) {
IMyxContainer container = MyxUtils.resolvePath(mainContainer, path);
if (container == null) {
throw new MyxInvalidPathException(path);
}
IMyxBrick b = container.getInternalBrick(brickName);
if (b == null) {
throw new IllegalArgumentException("No brick found with name: " + brickName + " at "
+ MyxUtils.pathToString(path));
}
return interfaceRepository.getAllInterfaceNames(b);
}
@Override
public IMyxInterfaceDescription getInterfaceDescription(List<? extends IMyxName> path, IMyxName brickName,
IMyxName interfaceName) {
IMyxContainer container = MyxUtils.resolvePath(mainContainer, path);
if (container == null) {
throw new MyxInvalidPathException(path);
}
IMyxBrick b = container.getInternalBrick(brickName);
if (b == null) {
throw new IllegalArgumentException("No brick found with name: " + brickName + " at "
+ MyxUtils.pathToString(path));
}
return interfaceRepository.getInterfaceDescription(b, interfaceName);
}
@Override
public EMyxInterfaceDirection getInterfaceDirection(List<? extends IMyxName> path, IMyxName brickName,
IMyxName interfaceName) {
IMyxContainer container = MyxUtils.resolvePath(mainContainer, path);
if (container == null) {
throw new MyxInvalidPathException(path);
}
IMyxBrick b = container.getInternalBrick(brickName);
if (b == null) {
throw new IllegalArgumentException("No brick found with name: " + brickName + " at "
+ MyxUtils.pathToString(path));
}
return interfaceRepository.getInterfaceDirection(b, interfaceName);
}
@Override
public void init(List<? extends IMyxName> path, IMyxName brickName) {
callLifecycleMethod(path, brickName, Operation.INIT);
}
@Override
public void begin(List<? extends IMyxName> path, IMyxName brickName) {
callLifecycleMethod(path, brickName, Operation.BEGIN);
}
@Override
public void end(List<? extends IMyxName> path, IMyxName brickName) {
callLifecycleMethod(path, brickName, Operation.END);
}
@Override
public void destroy(List<? extends IMyxName> path, IMyxName brickName) {
callLifecycleMethod(path, brickName, Operation.DESTROY);
}
private void callLifecycleMethod(List<? extends IMyxName> path, IMyxName brickName, Operation method) {
IMyxContainer container = MyxUtils.resolvePath(mainContainer, path);
if (container == null) {
throw new MyxInvalidPathException(path);
}
IMyxBrick b = null;
if (brickName == null) {
b = container;
}
else {
b = container.getInternalBrick(brickName);
if (b == null) {
throw new IllegalArgumentException("No brick found with name: " + brickName + " at "
+ MyxUtils.pathToString(path));
}
}
for (IMyxLifecycleProcessor lp : b.getLifecycleProcessors()) {
switch (method) {
case INIT:
lp.init();
break;
case BEGIN:
lp.begin();
break;
case END:
lp.end();
break;
case DESTROY:
lp.destroy();
break;
}
}
}
private static class MyxWeldData {
public final List<? extends IMyxBrick> requiredBricks;
public final List<? extends IMyxName> requiredInterfaces;
public final List<? extends IMyxBrick> providedBricks;
public final List<? extends IMyxName> providedInterfaces;
public MyxWeldData(List<? extends IMyxBrick> requiredBricks, List<? extends IMyxName> requiredInterfaces,
List<? extends IMyxBrick> providedBricks, List<? extends IMyxName> providedInterfaces) {
this.requiredBricks = requiredBricks;
this.requiredInterfaces = requiredInterfaces;
this.providedBricks = providedBricks;
this.providedInterfaces = providedInterfaces;
}
}
private MyxWeldData parseAndValidateWeld(IMyxWeld weld) {
synchronized (weldList) {
IMyxContainer container1 = MyxUtils.resolvePath(mainContainer, weld.getRequiredPath());
if (container1 == null) {
throw new MyxInvalidPathException(weld.getRequiredPath());
}
IMyxBrick b1 = container1.getInternalBrick(weld.getRequiredBrickName());
if (b1 == null) {
throw new IllegalArgumentException("No brick found with name: " + weld.getRequiredBrickName() + " at "
+ MyxUtils.pathToString(weld.getRequiredPath()));
}
IMyxName i1 = weld.getRequiredInterfaceName();
Collection<? extends IMyxName> names = interfaceRepository.getAllInterfaceNames(b1);
if (!names.contains(i1)) {
throw new IllegalArgumentException("No interface found with name: " + i1 + " on brick "
+ weld.getRequiredBrickName());
}
IMyxContainer container2 = MyxUtils.resolvePath(mainContainer, weld.getProvidedPath());
if (container2 == null) {
throw new MyxInvalidPathException(weld.getProvidedPath());
}
IMyxBrick b2 = container2.getInternalBrick(weld.getProvidedBrickName());
if (b2 == null) {
throw new IllegalArgumentException("No brick found with name: " + weld.getProvidedBrickName() + " at "
+ MyxUtils.pathToString(weld.getRequiredPath()));
}
IMyxName i2 = weld.getProvidedInterfaceName();
Collection<? extends IMyxName> names2 = interfaceRepository.getAllInterfaceNames(b2);
if (!names2.contains(i2)) {
throw new IllegalArgumentException("No interface found with name: " + i2 + " on brick "
+ weld.getProvidedBrickName());
}
//Okay, we've got all the data set up. Now we have to actually
//hook up the interfaces. If either the provided or required
//side is a container, that means we have to follow mappings down
//until we get to the real provider/requirer brick.
//1 == required; 2 == provided
IMyxBrick requiredBrick = b1;
List<IMyxBrick> rb = Collections.singletonList(requiredBrick);
IMyxName requiredInterface = i1;
List<IMyxName> ri = Collections.singletonList(requiredInterface);
while (requiredBrick instanceof IMyxContainer) {
Collection<? extends IMyxName> internalBrickNames = interfaceRepository.getInternalBrickName(
requiredBrick, requiredInterface);
ri = new ArrayList<IMyxName>(interfaceRepository.getInternalInterfaceName(requiredBrick,
requiredInterface));
rb = new ArrayList<IMyxBrick>(internalBrickNames.size());
for (IMyxName internalBrickName : internalBrickNames) {
rb.add(((IMyxContainer) requiredBrick).getInternalBrick(internalBrickName));
}
requiredBrick = rb.iterator().next();
}
IMyxBrick providedBrick = b2;
List<IMyxBrick> pb = Collections.singletonList(providedBrick);
IMyxName providedInterface = i2;
List<IMyxName> pi = Collections.singletonList(providedInterface);
while (providedBrick instanceof IMyxContainer) {
Collection<? extends IMyxName> internalBrickNames = interfaceRepository.getInternalBrickName(
providedBrick, providedInterface);
pi = new ArrayList<IMyxName>(interfaceRepository.getInternalInterfaceName(providedBrick,
providedInterface));
rb = new ArrayList<IMyxBrick>(internalBrickNames.size());
for (IMyxName internalBrickName : internalBrickNames) {
pb.add(((IMyxContainer) requiredBrick).getInternalBrick(internalBrickName));
}
providedBrick = pb.iterator().next();
}
//Wrap up the data
MyxWeldData mwd = new MyxWeldData(rb, ri, pb, pi);
return mwd;
}
}
@Override
public IMyxWeld createWeld(List<? extends IMyxName> requiredPath, IMyxName requiredBrickName,
IMyxName requiredInterfaceName, List<? extends IMyxName> providedPath, IMyxName providedBrickName,
IMyxName providedInterfaceName) {
return new MyxBasicWeld(requiredPath, requiredBrickName, requiredInterfaceName, providedPath,
providedBrickName, providedInterfaceName);
}
@Override
public void addWeld(IMyxWeld weld) {
synchronized (weldList) {
MyxWeldData mwd = parseAndValidateWeld(weld);
for (int i = 0; i < mwd.requiredBricks.size(); i++) {
IMyxBrick requiredBrick = mwd.requiredBricks.get(i);
IMyxBrickItems requiredBrickItems = requiredBrick.getMyxBrickItems();
if (requiredBrickItems == null) {
throw new RuntimeException("Brick " + MyxUtils.getName(requiredBrick) + " has no brick items.");
}
IMyxRequiredServiceProvider sp = requiredBrickItems.getRequiredServiceProvider();
if (sp == null) {
throw new RuntimeException("Brick " + MyxUtils.getName(requiredBrick)
+ " has a null required service provider.");
}
if (!(sp instanceof MyxBasicRequiredServiceProvider)) {
throw new RuntimeException("Brick " + MyxUtils.getName(requiredBrick)
+ " has an invalid required service provider.");
}
MyxBasicRequiredServiceProvider bsp = (MyxBasicRequiredServiceProvider) sp;
for (int j = 0; j < mwd.providedBricks.size(); j++) {
IMyxBrick providedBrick = mwd.providedBricks.get(j);
IMyxProvidedServiceProvider providedServiceProvider = providedBrick.getProvidedServiceProvider();
Object providedServiceObject = providedServiceProvider.getServiceObject(mwd.providedInterfaces
.get(j));
bsp.addService(mwd.requiredInterfaces.get(i), providedServiceObject);
//Notify the brick of the connection if it's dynamic
if (mwd.requiredBricks.get(i) instanceof IMyxDynamicBrick) {
((IMyxDynamicBrick) mwd.requiredBricks.get(i)).interfaceConnected(
mwd.requiredInterfaces.get(i), providedServiceObject);
}
}
}
weldList.add(weld);
}
}
@Override
public void removeWeld(IMyxWeld weld) {
synchronized (weldList) {
MyxWeldData mwd = parseAndValidateWeld(weld);
for (int i = 0; i < mwd.requiredBricks.size(); i++) {
IMyxBrickItems requiredBrickItems = mwd.requiredBricks.get(i).getMyxBrickItems();
if (requiredBrickItems == null) {
throw new RuntimeException("Brick " + MyxUtils.getName(mwd.requiredBricks.get(i))
+ " has no brick items.");
}
IMyxRequiredServiceProvider sp = requiredBrickItems.getRequiredServiceProvider();
if (sp == null) {
throw new RuntimeException("Brick " + MyxUtils.getName(mwd.requiredBricks.get(i))
+ " has a null required service provider.");
}
if (!(sp instanceof MyxBasicRequiredServiceProvider)) {
throw new RuntimeException("Brick " + MyxUtils.getName(mwd.requiredBricks.get(i))
+ " has an invalid required service provider.");
}
MyxBasicRequiredServiceProvider bsp = (MyxBasicRequiredServiceProvider) sp;
for (int j = 0; j < mwd.providedBricks.size(); j++) {
IMyxProvidedServiceProvider providedServiceProvider = mwd.providedBricks.get(j)
.getProvidedServiceProvider();
Object providedServiceObject = providedServiceProvider.getServiceObject(mwd.providedInterfaces
.get(j));
bsp.removeService(mwd.requiredInterfaces.get(i), providedServiceObject);
//Notify the brick of the disconnection if it's dynamic
if (mwd.requiredBricks.get(i) instanceof IMyxDynamicBrick) {
((IMyxDynamicBrick) mwd.requiredBricks.get(i)).interfaceDisconnected(
mwd.requiredInterfaces.get(i), providedServiceObject);
}
}
}
weldList.remove(weld);
}
}
@Override
public Collection<? extends IMyxWeld> getAllWelds() {
synchronized (weldList) {
return Collections.unmodifiableList(new ArrayList<IMyxWeld>(weldList));
}
}
}