* @param aService
* Service.
*/
- protected final void addService(
+ protected final void addInterface(
ProvidedInterface aDescriptor, Object aService) {
LOG.info("Interface '" + getQualifiedName() + "." + aDescriptor.getName() + "' started.");
_running.add(aDescriptor);
}
@Override
- public ProvidedInterface[] getRunningServices() {
+ public ProvidedInterface[] getRunningInterfaces() {
return _running.toArray(new ProvidedInterface[0]);
}
@Override
public void stop() {
- doStop();
+ doStop();
+ if ( _running.size() > 0 ) {
+ // programming error.
+ throw new RuntimeException(getQualifiedName() + ": still services running after the stop call.");
+ }
_status = Status.STOPPED;
}
- protected abstract void doStop();
+ protected abstract void doStop();
+
+ /**
+ * Implementations must call this method to indicate that a running service has
+ * been stopped.
+ *
+ * @param aService
+ * Service.
+ */
+ protected final void removeInterface(
+ ProvidedInterface aDescriptor) {
+ LOG.info("Interface '" + getQualifiedName() + "." + aDescriptor.getName() + "' stopped.");
+ _running.remove(aDescriptor);
+ aDescriptor.publish(null);
+ }
@Override
public String toString() {
* {@link #initialize(String, Service[])} has been called.
* @return
*/
- ProvidedInterface[] getRunningServices();
+ ProvidedInterface[] getRunningInterfaces();
/**
* Stops a subsystem.
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- */
+ */
package org.wamblee.system.core;
import java.util.ArrayList;
import org.apache.commons.logging.LogFactory;
/**
- * Composite system consisting of multiple subsystems.
- *
+ * Composite system consisting of multiple subsystems.
+ *
* @author Erik Brakkee
*/
public class Container extends AbstractComponent {
}
public static ProvidedInterface[] filterProvidedServices(
- RequiredInterface aRequired,
- Collection<ProvidedInterface> aProvided) {
+ RequiredInterface aRequired, Collection<ProvidedInterface> aProvided) {
List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
for (ProvidedInterface descriptor : aProvided) {
if (aRequired.implementedBy(descriptor)) {
}
return provided.toArray(new ProvidedInterface[0]);
}
-
+
/**
- * Construcst the composite system.
- * @param aName Name of the system.
- * @param aRegistry Service registry.
- * @param aSystems Subsystems.
- * @param aProvided Provided services of the system.
- * @param aRequired Required services by the system.
+ * Construcst the composite system.
+ *
+ * @param aName
+ * Name of the system.
+ * @param aRegistry
+ * Service registry.
+ * @param aSystems
+ * Subsystems.
+ * @param aProvided
+ * Provided services of the system.
+ * @param aRequired
+ * Required services by the system.
*/
public Container(String aName, Component[] aSystems,
ProvidedInterface[] aProvided, RequiredInterface[] aRequired) {
super(aName, aProvided, aRequired);
_systems = aSystems;
- for (Component component: aSystems) {
+ for (Component component : aSystems) {
component.addContext(getQualifiedName());
}
validate(aRequired);
}
/**
- * Validates the subsystems together to check that there are
- * no required services not in the required list and
- * no services in the provided list that cannot be provided.
- * Also logs a warning in case of superfluous requirements.
+ * Validates the subsystems together to check that there are no required
+ * services not in the required list and no services in the provided list
+ * that cannot be provided. Also logs a warning in case of superfluous
+ * requirements.
*/
private void validate(RequiredInterface[] aRequired) {
List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
for (ProvidedInterface service : getProvidedInterfaces()) {
if (!(provided.contains(service))) {
- throw new SystemAssemblyException(getName() + ": Service '" + service
+ throw new SystemAssemblyException(getName() + ": Service '"
+ + service
+ "' is not provided by any of the subsystems");
}
}
required);
// Compute all required interfaces that are not provided
for (ProvidedInterface service : provided) {
- List<RequiredInterface> fulfilled =
- Arrays.asList(filterRequiredServices(service,
- reallyRequired));
- reallyRequired.removeAll(fulfilled);
+ List<RequiredInterface> fulfilled = Arrays
+ .asList(filterRequiredServices(service, reallyRequired));
+ reallyRequired.removeAll(fulfilled);
}
// Now the remaining interfaces should be covered by the required
- // list.
+ // list.
reallyRequired.removeAll(Arrays.asList(aRequired));
-
+
String missingRequired = "";
- for (RequiredInterface service: reallyRequired) {
+ for (RequiredInterface service : reallyRequired) {
missingRequired += service + "\n";
}
- if ( missingRequired.length() > 0 ) {
- throw new SystemAssemblyException(getName() + ": missing required services\n" + missingRequired);
+ if (missingRequired.length() > 0) {
+ throw new SystemAssemblyException(getName()
+ + ": missing required services\n" + missingRequired);
}
}
@Override
protected void doStart() {
List<ProvidedInterface> provided = 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 ) {
- throw new SystemAssemblyException(getQualifiedName() + ": required interface '" + intf +"' is not provided");
- }
+ for (RequiredInterface intf : required) {
+ ProvidedInterface provider = intf.getProvider();
+ if (provider == null) {
+ throw new SystemAssemblyException(getQualifiedName()
+ + ": required interface '" + intf + "' is not provided");
+ }
provided.add(intf.getProvider());
}
-
+
startImpl();
}
-
+
/**
* Starts the subsystems.
*
private void startImpl() {
LOG.info("Starting '" + getQualifiedName() + "'");
List<ProvidedInterface> allProvided = new ArrayList<ProvidedInterface>();
-
- // Add the provides of all externally required interfaces to the list of available
+
+ // Add the provides of all externally required interfaces to the list of
+ // available
// interfaces
- for (RequiredInterface required: getRequiredInterfaces()) {
+ for (RequiredInterface required : getRequiredInterfaces()) {
allProvided.add(required.getProvider());
}
-
+
+ List<Component> started = new ArrayList<Component>();
for (Component system : _systems) {
- // Check if all required services are already provided by earlier
- // systems.
- RequiredInterface[] required = system.getRequiredInterfaces();
-
- for (RequiredInterface descriptor : required) {
- ProvidedInterface[] filtered = filterProvidedServices(
- descriptor, allProvided);
-
- if (filtered.length == 0) {
- throw new SystemAssemblyException(
- "Service '"
- + descriptor
- + "' required by system '"
- + system
- + "' is not provided by systems that are started earlier");
+ try {
+ // Check if all required services are already provided by
+ // earlier
+ // systems.
+ RequiredInterface[] required = system.getRequiredInterfaces();
+
+ for (RequiredInterface descriptor : required) {
+ ProvidedInterface[] filtered = filterProvidedServices(
+ descriptor, allProvided);
+
+ if (filtered.length == 0) {
+ throw new SystemAssemblyException(
+ "Service '"
+ + descriptor
+ + "' required by system '"
+ + system
+ + "' is not provided by systems that are started earlier");
+ }
+ if (filtered.length > 1) {
+ throw new SystemAssemblyException(
+ "Service '"
+ + descriptor
+ + "' required by system '"
+ + system
+ + "' matches multiple services provided by other systems: "
+ + Arrays.asList(filtered));
+ }
+ descriptor.setProvider(filtered[0]);
}
- if (filtered.length > 1) {
- throw new SystemAssemblyException(
- "Service '"
- + descriptor
- + "' required by system '"
- + system
- + "' matches multiple services provided by other systems: " +
- Arrays.asList(filtered));
+
+ // Start the service.
+ system.start();
+ started.add(system);
+
+ // add all provided services
+ ProvidedInterface[] provided = system.getProvidedInterfaces();
+ allProvided.addAll(Arrays.asList(provided));
+ } catch (SystemAssemblyException e) {
+ throw e;
+ } catch (RuntimeException e) {
+ LOG.error(getQualifiedName() + ": could not start '"
+ + system.getQualifiedName() + "'", e);
+ // an exception occurred, stop the successfully started
+ // systems
+ for (int i = started.size() - 1; i >= 0; i--) {
+ try {
+ started.get(i).stop();
+ } catch (Throwable t) {
+ LOG.error(getQualifiedName() + ": error stopping "
+ + started.get(i).getQualifiedName());
+ }
}
- descriptor.setProvider(filtered[0]);
+ throw e;
}
-
- // Start the service.
- system.start();
-
- // add all provided services
- ProvidedInterface[] provided = system.getProvidedInterfaces();
- allProvided.addAll(Arrays.asList(provided));
}
+
}
-
@Override
protected void doStop() {
- for (int i = _systems.length-1; i >= 0; i--) {
+ for (int i = _systems.length - 1; i >= 0; i--) {
_systems[i].stop();
}
}
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- */
+ */
package org.wamblee.system.core;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
+import org.easymock.classextension.ConstructorArgs;
+import org.easymock.classextension.EasyMock;
+import org.easymock.classextension.IMocksControl;
import org.wamblee.system.core.Component;
import org.wamblee.system.core.Container;
import org.wamblee.system.core.DefaultProvidedInterface;
ProvidedInterface prov3 = new DefaultProvidedInterface("name",
MyMultiple.class);
- AssertionUtils.assertEquals(new RequiredInterface[] { req1 },
- Container.filterRequiredServices(prov1, Arrays
+ AssertionUtils.assertEquals(new RequiredInterface[] { req1 }, Container
+ .filterRequiredServices(prov1, Arrays
.asList(new RequiredInterface[] { req1 })));
- AssertionUtils.assertEquals(new RequiredInterface[] { req1 },
- Container.filterRequiredServices(prov1, Arrays
+ AssertionUtils.assertEquals(new RequiredInterface[] { req1 }, Container
+ .filterRequiredServices(prov1, Arrays
.asList(new RequiredInterface[] { req1, req2 })));
AssertionUtils.assertEquals(new RequiredInterface[] { req1, req2 },
Container.filterRequiredServices(prov3, Arrays
Container container = new Container("root", new Component[] {
environment, application }, new ProvidedInterface[0],
new RequiredInterface[0]);
-
+
container.start();
AssertionUtils.assertEquals(new String[] { "start.environment",
"start.application" }, _tracker.getEvents(
Thread.currentThread()).toArray(new String[0]));
- ProvidedInterface[] envServices = environment.getRunningServices();
+ ProvidedInterface[] envServices = environment.getRunningInterfaces();
assertEquals(2, envServices.length);
- ProvidedInterface[] appServices = environment.getRunningServices();
- assertEquals(2, appServices.length);
-
+ ProvidedInterface[] appServices = application.getRunningInterfaces();
+ assertEquals(0, appServices.length);
+
}
public void testApplicationEnvironment() {
try {
Component environment = new Environment();
Component application = new Application();
- Container container = new Container(
- "root",
- new Component[] {
- application, environment },
- new ProvidedInterface[0], new RequiredInterface[0]);
+ Container container = new Container("root", new Component[] {
+ application, environment }, new ProvidedInterface[0],
+ new RequiredInterface[0]);
container.start();
} catch (SystemAssemblyException e) {
// e.printStackTrace();
assertEquals(0, _tracker.getEventCount());
assertEquals(Status.NOT_STARTED, environment.getStatus());
assertEquals(Status.NOT_STARTED, application.getStatus());
-
+
Container system = new Container("all", new Component[] { environment,
application }, new ProvidedInterface[0],
new RequiredInterface[0]);
assertEquals(Status.RUNNING, environment.getStatus());
assertEquals(Status.RUNNING, application.getStatus());
assertEquals(Status.RUNNING, system.getStatus());
-
- AssertionUtils.assertEquals(
- new String[] { "start.environment", "start.application" },
- _tracker.getEvents(Thread.currentThread()).toArray(new String[0]));
- _tracker.clear();
-
+
+ AssertionUtils.assertEquals(new String[] { "start.environment",
+ "start.application" }, _tracker.getEvents(
+ Thread.currentThread()).toArray(new String[0]));
+ _tracker.clear();
+
system.stop();
assertEquals(Status.STOPPED, environment.getStatus());
assertEquals(Status.STOPPED, application.getStatus());
assertEquals(Status.STOPPED, system.getStatus());
-
- AssertionUtils.assertEquals(
- new String[] { "stop.application", "stop.environment" },
- _tracker.getEvents(Thread.currentThread()).toArray(new String[0]));
-
-
+
+ AssertionUtils.assertEquals(new String[] { "stop.application",
+ "stop.environment" }, _tracker
+ .getEvents(Thread.currentThread()).toArray(new String[0]));
+
}
public void testCompositeWithWrongProvidedInfo() {
fail();
}
+ public void testEnvironmentApplicationRollbackOnException()
+ throws Exception {
+ IMocksControl control = EasyMock.createStrictControl();
+
+ Environment environment = new Environment(_tracker);
+ Application application = control.createMock(Application.class,
+ new ConstructorArgs(Application.class.getConstructor()),
+ Application.class.getDeclaredMethod("doStart"));
+
+ application.doStart();
+ EasyMock.expectLastCall().andThrow(new RuntimeException());
+ control.replay();
+
+ try {
+ Container container = new Container("root", new Component[] {
+ environment, application }, new ProvidedInterface[0],
+ new RequiredInterface[0]);
+
+ container.start();
+ } catch (RuntimeException e) {
+ AssertionUtils.assertEquals(new String[] { "start.environment",
+ "stop.environment" }, _tracker.getEvents(
+ Thread.currentThread()).toArray(new String[0]));
+ ProvidedInterface[] envServices = environment.getRunningInterfaces();
+ assertEquals(0, envServices.length);
+ return;
+ }
+ fail();
+ }
+
+ public void testEnvironmentApplicationRollbackOnExceptionWithExceptionOnStop()
+ throws Exception {
+ IMocksControl control = EasyMock.createControl();
+
+ Environment environment = new Environment(_tracker);
+ // Application 1 will throw an exception while stopping.
+ Application application1 = control.createMock(Application.class,
+ new ConstructorArgs(Application.class.getConstructor()),
+ Application.class.getDeclaredMethod("doStop"));
+
+ application1.doStop();
+ EasyMock.expectLastCall().andThrow(new RuntimeException());
+
+ // application 2 will throw an exception while starting
+ Application application2 = control.createMock(Application.class,
+ new ConstructorArgs(Application.class.getConstructor()),
+ Application.class.getDeclaredMethod("doStart"));
+
+ application2.doStart();
+ EasyMock.expectLastCall().andThrow(new RuntimeException());
+
+ control.replay();
+
+ try {
+ Container container = new Container("root", new Component[] {
+ environment, application1, application2 }, new ProvidedInterface[0],
+ new RequiredInterface[0]);
+
+ container.start();
+ } catch (RuntimeException e) {
+ AssertionUtils.assertEquals(new String[] { "start.environment",
+ "stop.environment" }, _tracker.getEvents(
+ Thread.currentThread()).toArray(new String[0]));
+ ProvidedInterface[] envServices = environment.getRunningInterfaces();
+ assertEquals(0, envServices.length);
+ return;
+ }
+ fail();
+ }
}
@Override
protected void doStart() {
- addService(getProvidedInterfaces()[0], new Integer(1));
- addService(getProvidedInterfaces()[1], new Integer(2));
+ addInterface(getProvidedInterfaces()[0], new Integer(1));
+ addInterface(getProvidedInterfaces()[1], new Integer(2));
track("start." + getName());
}
@Override
protected void doStop() {
track("stop." + getName());
+ removeInterface(getProvidedInterfaces()[0]);
+ removeInterface(getProvidedInterfaces()[1]);
}
private void track(String aString) {
throw new IllegalArgumentException(getQualifiedName() + ": service '"
+ name + "' is null");
}
- addService(_provided.get(name), svc);
+ addInterface(_provided.get(name), svc);
}
}
@Override
protected void doStop() {
_context.close();
+ for (ProvidedInterface provided: getProvidedInterfaces()) {
+ removeInterface(provided);
+ }
}
}
new HashMap<String, ProvidedInterface>(),
new HashMap<RequiredInterface, String>());
system.start();
- ProvidedInterface[] services = system.getRunningServices();
+ ProvidedInterface[] services = system.getRunningInterfaces();
assertEquals(0, services.length);
system.stop();
new String[] { HELLO_SERVICE_SPRING_XML }, provided,
new HashMap<RequiredInterface, String>());
system.start();
- ProvidedInterface[] services = system.getRunningServices();
+ ProvidedInterface[] services = system.getRunningInterfaces();
assertEquals(1, services.length);
assertTrue(services[0].getImplementation() instanceof HelloService);
assertEquals("Hello world!", ((HelloService)services[0].getImplementation())
system.addProperties(props);
system.start();
- ProvidedInterface[] services = system.getRunningServices();
+ ProvidedInterface[] services = system.getRunningInterfaces();
assertEquals("Property Value",
((HelloService)services[0].getImplementation()).say());
}