import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.wamblee.collections.CollectionFilter;
+import org.wamblee.conditions.Condition;
+import org.wamblee.general.Pair;
/**
* Container consisting of multiple components.
private List<Component> _components;
private Set<String> _componentNames;
+ private CompositeInterfaceRestriction _restriction;
private boolean _sealed;
-
- public static RequiredInterface[] filterRequiredServices(
- ProvidedInterface aProvided,
- Collection<RequiredInterface> aDescriptors) {
- List<RequiredInterface> required = new ArrayList<RequiredInterface>();
- for (RequiredInterface descriptor : aDescriptors) {
- if (descriptor.implementedBy(aProvided)) {
- required.add(descriptor);
+
+ static ProvidedInterface[] filterProvidedServices(
+ Component aClient, RequiredInterface aRequired, Collection<Pair<ProvidedInterface,Component>> aProvided,
+ InterfaceRestriction aRestriction) {
+ List<ProvidedInterface> result = new ArrayList<ProvidedInterface>();
+ for (Pair<ProvidedInterface,Component> descriptor : aProvided) {
+ ProvidedInterface provided = descriptor.getFirst();
+ Component server = descriptor.getSecond();
+ if (aRequired.implementedBy(provided) &&
+ !aRestriction.isViolated(aClient, aRequired, server, provided)) {
+ result.add(provided);
}
}
- return required.toArray(new RequiredInterface[0]);
- }
-
- public static ProvidedInterface[] filterProvidedServices(
- RequiredInterface aRequired, Collection<ProvidedInterface> aProvided) {
- List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
- for (ProvidedInterface descriptor : aProvided) {
- if (aRequired.implementedBy(descriptor)) {
- provided.add(descriptor);
- }
- }
- return provided.toArray(new ProvidedInterface[0]);
+ return result.toArray(new ProvidedInterface[0]);
}
/**
ProvidedInterface[] aProvided, RequiredInterface[] aRequired) {
super(aName, aProvided, aRequired);
_components = new ArrayList<Component>();
-
+
_componentNames = new HashSet<String>();
+ _restriction = new CompositeInterfaceRestriction();
_sealed = false;
for (Component component : aComponents) {
addComponent(component);
}
- validate();
}
public Container(String aName) {
aComponent.addContext(getQualifiedName());
return this;
}
+
+ /**
+ * Adds an interface restriction for explicitly configuring the
+ * relations between components.
+ * @param aRestriction Restriction to add.
+ * @return Reference to this to allow call chaining.
+ */
+ public Container addRestriction(InterfaceRestriction aRestriction) {
+ checkSealed();
+ _restriction.add(aRestriction);
+ return this;
+ }
@Override
public Container addProvidedInterface(ProvidedInterface aProvided) {
super.addRequiredInterface(aRequired);
return this;
}
+
+ @Override
+ public void addContext(String aContext) {
+ super.addContext(aContext);
+ for (Component component: _components) {
+ component.addContext(aContext);
+ }
+ }
/**
* Validates the components together to check that there are no required
* in case of any validation problems.
*/
public void validate() {
- List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
- for (Component component : _components) {
- provided.addAll(Arrays.asList(component.getProvidedInterfaces()));
- }
-
- List<RequiredInterface> required = new ArrayList<RequiredInterface>();
- for (Component component : _components) {
- required.addAll(Arrays.asList(component.getRequiredInterfaces()));
- }
-
- validateProvidedInterfaces(provided);
+ validateProvidedInterfacesArePresent();
- validateRequiredInterfaces(required);
+ validateRequiredInterfaces();
- List<RequiredInterface> reallyRequired = validateRequiredProvidedMatch(
- provided, required);
-
- String missingRequired = "";
- for (RequiredInterface service : reallyRequired) {
- missingRequired += service + "\n";
- }
- if (missingRequired.length() > 0) {
- throw new SystemAssemblyException(getName()
- + ": missing required services\n" + missingRequired);
- }
+ doStartOptionalDryRun(null, true);
}
- private List<RequiredInterface> validateRequiredProvidedMatch(
- List<ProvidedInterface> aProvided, List<RequiredInterface> aRequired) {
- List<RequiredInterface> reallyRequired = new ArrayList<RequiredInterface>(
- aRequired);
- // Compute all required interfaces that are not provided
-
- for (ProvidedInterface service : aProvided) {
- List<RequiredInterface> fulfilled = Arrays
- .asList(filterRequiredServices(service, reallyRequired));
- reallyRequired.removeAll(fulfilled);
- }
- // Now remove all optional interfaces from the list.
- for (Iterator<RequiredInterface> i = reallyRequired.iterator(); i
- .hasNext();) {
- RequiredInterface req = i.next();
- if (req.isOptional()) {
- i.remove();
- }
+ private void validateRequiredInterfaces() {
+ List<RequiredInterface> required = new ArrayList<RequiredInterface>();
+ for (Component component : _components) {
+ required.addAll(Arrays.asList(component.getRequiredInterfaces()));
}
- // Now the remaining interfaces should be covered by the required
- // list.
- reallyRequired.removeAll(Arrays.asList(getRequiredInterfaces()));
- return reallyRequired;
- }
- private void validateRequiredInterfaces(List<RequiredInterface> aRequired) {
for (RequiredInterface service : getRequiredInterfaces()) {
// TODO required interfaces by the component could be
// subclasses or implementations of the requirements
// of the contained components. The code below assumes
// an exact match.
- if (!(aRequired.contains(service))) {
+ if (!(required.contains(service))) {
info("Service '"
+ service
+ "' indicated as required is not actually required by any of the components");
// is optional whereas the internally required service is
// mandatory.
if (service.isOptional()) {
- for (RequiredInterface intf : aRequired) {
+ for (RequiredInterface intf : required) {
if (intf.equals(service) && !intf.isOptional()) {
warn("Required service '"
+ service
}
}
- private void validateProvidedInterfaces(List<ProvidedInterface> aProvided) {
+ private void validateProvidedInterfacesArePresent() {
for (ProvidedInterface service : getProvidedInterfaces()) {
- // TODO provided interfaces by components could be
- // provide subclasses or implementations of the
- // provided interfaces of the container.
- // The code below assumes an exact match.
- if (!(aProvided.contains(service))) {
- throw new SystemAssemblyException(getName() + ": Service '"
- + service
- + "' is not provided by any of its components");
- }
+ findProvidedInterface(service);
}
}
-
+
/**
- * Starts the container. After the container is started, the container
- * becomes sealed meaning that no further components, required or provided
- * interfaces may be added.
- *
- * @return Scope.
+ * Finds the component and provided interface that matches a provided interface of this
+ * container.
+ * @param aProvided Interface to provide externally.
+ * @return Pair of component and provided interface
+ * @throws SystemAssemblyException In case there are multiple matches or no match at all.
*/
- public Scope start() {
- checkSealed();
- validate();
- Scope scope = super.start(new DefaultScope(new ProvidedInterface[0]));
- seal();
- return scope;
+ private Pair<Component,ProvidedInterface> findProvidedInterface(ProvidedInterface aProvided) {
+ List<Pair<Component,ProvidedInterface>> result =
+ new ArrayList<Pair<Component,ProvidedInterface>>();
+ for (Component component: _components) {
+ for (ProvidedInterface provided: component.getProvidedInterfaces()) {
+ if ( aProvided.equals(provided) ) {
+ result.add(new Pair<Component,ProvidedInterface>(component, provided));
+ }
+ }
+ }
+ if ( result.size() == 0) {
+ throw new SystemAssemblyException(getQualifiedName() + ": Service '"
+ + aProvided
+ + "' is not provided by any of its components");
+ }
+ if ( result.size() > 1) {
+ throw new SystemAssemblyException(getQualifiedName() + ": Service '"
+ + aProvided
+ + "' is provided by multiple components: " + result);
+ }
+ return result.get(0);
}
+
/**
* Seal the container, meaning that no further components or interfaces may
* be added.
public boolean isSealed() {
return _sealed;
}
+
+ /**
+ * Utility method to start with an empty external scope. This is useful for
+ * top-level containers which are not part of another container.
+ * @return Scope.
+ */
+ public Scope start() {
+ Scope scope = new DefaultScope(getProvidedInterfaces());
+ return super.start(scope);
+ }
@Override
protected Scope doStart(Scope aExternalScope) {
- LOG.info("Starting '" + getQualifiedName() + "'");
-
+ checkSealed();
+ validate();
Scope scope = new DefaultScope(getProvidedInterfaces(), aExternalScope);
+ doStartOptionalDryRun(scope, false);
+ exposeProvidedInterfaces(aExternalScope, scope);
+ seal();
+ return scope;
+ }
- List<ProvidedInterface> allProvided = new ArrayList<ProvidedInterface>();
-
- // all interfaces from the required list of this container are
- // provided to the components inside it.
- RequiredInterface[] required = getRequiredInterfaces();
- for (RequiredInterface intf : required) {
- ProvidedInterface provider = intf.getProvider();
- if (provider != null) {
- allProvided.add(provider);
- } else {
- if (!intf.isOptional()) {
- throw new SystemAssemblyException(getQualifiedName()
- + ": required interface '" + intf
- + "' is not provided");
- }
- }
+ private void exposeProvidedInterfaces(Scope aExternalScope, Scope aInternalScope) {
+ for (ProvidedInterface intf: getProvidedInterfaces()) {
+ Pair<Component, ProvidedInterface> found = findProvidedInterface(intf);
+ Object svc = aInternalScope.getInterfaceImplementation(found.getSecond(), Object.class);
+ addInterface(intf, svc, aExternalScope);
}
+ }
+
+ private void doStartOptionalDryRun(Scope aScope, boolean aDryRun) {
+ LOG.info("Starting '" + getQualifiedName() + "'");
+
+ List<Pair<ProvidedInterface,Component>> allProvided = new ArrayList<Pair<ProvidedInterface,Component>>();
+
+ addProvidersOfRequiredInterfaces(allProvided);
List<Component> started = new ArrayList<Component>();
for (Component component : _components) {
try {
- checkAllRequiredServicesAlreadyProvided(allProvided, component);
+ initializeProvidersForRequiredInterfaces(allProvided,
+ component, aDryRun);
// Start the service.
- Object runtime = component.start(scope);
- scope.addRuntime(component, runtime);
- started.add(component);
+ if (!aDryRun) {
+ Object runtime = component.start(aScope);
+ aScope.addRuntime(component, runtime);
+ started.add(component);
+ }
// add all provided services
ProvidedInterface[] provided = component
.getProvidedInterfaces();
- allProvided.addAll(Arrays.asList(provided));
+ for (ProvidedInterface prov: provided) {
+ allProvided.add(new Pair<ProvidedInterface,Component>(prov, component));
+ }
} catch (SystemAssemblyException e) {
throw e;
} catch (RuntimeException e) {
LOG.error(getQualifiedName() + ": could not start '"
+ component.getQualifiedName() + "'", e);
- stopAlreadyStartedComponents(started, scope);
+ stopAlreadyStartedComponents(started, aScope);
throw e;
}
}
- return scope;
+ }
+
+ private void addProvidersOfRequiredInterfaces(
+ List<Pair<ProvidedInterface,Component>> allProvided) {
+ // all interfaces from the required list of this container are
+ // provided to the components inside it.
+ RequiredInterface[] required = getRequiredInterfaces();
+ for (RequiredInterface intf : required) {
+ ProvidedInterface provider = intf.getProvider();
+ if (provider != null) {
+ allProvided.add(new Pair<ProvidedInterface,Component>(provider, null));
+ } else {
+ if (!intf.isOptional()) {
+ throw new SystemAssemblyException(getQualifiedName()
+ + ": required interface '" + intf
+ + "' is not provided");
+ }
+ }
+ }
}
private void stopAlreadyStartedComponents(List<Component> aStarted,
// components
for (int i = aStarted.size() - 1; i >= 0; i--) {
try {
- aStarted.get(i).stop(aScope);
+ Component component = aStarted.get(i);
+ aStarted.get(i).stop(aScope.getRuntime(component));
} catch (Throwable t) {
LOG.error(getQualifiedName() + ": error stopping "
+ aStarted.get(i).getQualifiedName());
}
}
- private void checkAllRequiredServicesAlreadyProvided(
- List<ProvidedInterface> aAllProvided, Component aComponent) {
+ /**
+ * Sets the provided interface or a component.
+ *
+ * @param aAllProvided
+ * All available provided interfaces.
+ * @param aComponent
+ * Component whose required interfaces we are looking at.
+ * @param aValidateOnly
+ * If true then the provider will not be set for required
+ * interfaces.
+ */
+ private void initializeProvidersForRequiredInterfaces(
+ List<Pair<ProvidedInterface,Component>> aAllProvided, Component aComponent,
+ boolean aValidateOnly) {
// Check if all required services are already provided by
// earlier
// systems.
for (RequiredInterface descriptor : aComponent.getRequiredInterfaces()) {
- ProvidedInterface[] filtered = filterProvidedServices(descriptor,
- aAllProvided);
+ ProvidedInterface[] filtered = filterProvidedServices(aComponent, descriptor,
+ aAllProvided, _restriction);
if (filtered.length == 1) {
- descriptor.setProvider(filtered[0]);
+ if (!aValidateOnly) {
+ descriptor.setProvider(filtered[0]);
+ }
} else if (filtered.length > 1) {
throw new SystemAssemblyException(
"Service '"