Fixed various problems in the component mechanism itself.
* Initialises the subsystem by starting all the services that
* it described as provided.
* @param aScope Scope with external interface implementations that are available. The component
- * implementation can either oublish itself in this scope or it can decide to
- * create a new scope with the scope passed in as a parent.
+ * must publish its runtime and its provided interfaces in this scope.
* @return Gets an object representing the runtime of the component.
*/
Type start(Scope aScope);
// provided interfaces of the container.
// The code below assumes an exact match.
if (!(provided.contains(service))) {
- throw new SystemAssemblyException(getName() + ": Service '"
+ throw new SystemAssemblyException(getQualifiedName() + ": Service '"
+ service
+ "' is not provided by any of its components");
}
}
}
- /**
- * 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.
- */
- public Scope start() {
- checkSealed();
- validate();
- Scope scope = super.start(new DefaultScope(new ProvidedInterface[0]));
- seal();
- return scope;
- }
-
/**
* 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) {
- return doStartOptionalDryRun(aExternalScope, false);
+ checkSealed();
+ validate();
+ Scope scope = new DefaultScope(getProvidedInterfaces(), aExternalScope);
+ doStartOptionalDryRun(scope, false);
+ seal();
+ return scope;
}
- private Scope doStartOptionalDryRun(Scope aExternalScope, boolean aDryRun) {
+ private void doStartOptionalDryRun(Scope aScope, boolean aDryRun) {
LOG.info("Starting '" + getQualifiedName() + "'");
- Scope scope = new DefaultScope(getProvidedInterfaces(), aExternalScope);
-
List<Pair<ProvidedInterface,Component>> allProvided = new ArrayList<Pair<ProvidedInterface,Component>>();
addProvidersOfRequiredInterfaces(allProvided);
// Start the service.
if (!aDryRun) {
- Object runtime = component.start(scope);
- scope.addRuntime(component, runtime);
+ Object runtime = component.start(aScope);
+ aScope.addRuntime(component, runtime);
started.add(component);
}
} 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(
*/
package org.wamblee.system.core;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
/**
* Default implementation of a service descriptor.
}
return buf.toString();
}
+
+ @Override
+ public boolean equals(Object aObj) {
+ if ( !(aObj instanceof DefaultProvidedInterface)) {
+ return false;
+ }
+ DefaultProvidedInterface provided = (DefaultProvidedInterface)aObj;
+ return getEqualsRepresentation().equals(provided.getEqualsRepresentation());
+ }
+
+ @Override
+ public int hashCode() {
+ return getEqualsRepresentation().hashCode();
+ }
+
+
+ private String getEqualsRepresentation() {
+ List<String> result = new ArrayList<String>();
+ for (Class cls: _interfaces) {
+ result.add(cls.getName());
+ }
+ Collections.sort(result);
+ String value = "";
+ for (String str: result) {
+ value += ":" + str;
+ }
+ return value;
+ }
}
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
+import java.util.UUID;
public class DefaultScope implements Scope {
private List<Scope> _parents;
- private int _count;
private Map<String, Object> _properties;
private Map<String, Object> _runtimes;
private Map<String, ProvidedInterfaceImplementation> _provided;
public DefaultScope(ProvidedInterface[] aExternallyProvided,
List<Scope> aParent) {
_parents = new ArrayList<Scope>(aParent);
- _count = 0;
_properties = new HashMap<String, Object>();
_runtimes = new HashMap<String, Object>();
_provided = new HashMap<String, ProvidedInterfaceImplementation>();
@Override
synchronized public void publishInterface(ProvidedInterface aInterface,
Object aImplementation) {
- String id = "" + _count++;
+ String id = UUID.randomUUID().toString();
_provided.put(id, new ProvidedInterfaceImplementation(aInterface,
aImplementation));
aInterface.setUniqueId(id);
assertEquals(env1.getString(), app.getString());
assertFalse(env2.getString().equals(app.getString()));
}
+
+ public void testProvidedInDifferentScopes() {
+ // Scoping problem occurred. Externally and internally provided components clashed
+ // because unique id generation in the scope was wrong.
+
+ StringComponent str = new StringComponent("string");
+ Application app = new Application("app");
+ Container container = new Container("top").addComponent(str).addComponent(app);
+ container.addRequiredInterface(new DefaultRequiredInterface("integer", Integer.class));
+
+ ProvidedInterface provided = new DefaultProvidedInterface("hallo", Integer.class);
+ container.getRequiredInterfaces()[0]
+ .setProvider(provided);
+
+ Scope external = new DefaultScope(new ProvidedInterface[0]);
+ external.publishInterface(provided, 100);
+ Scope scope = container.start(external);
+ }
}
--- /dev/null
+/*
+ * Copyright 2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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 junit.framework.TestCase;
+
+public class DefaultScopeTest extends TestCase {
+
+ public void testLookup() {
+ ProvidedInterface provider = new DefaultProvidedInterface("x", Integer.class);
+ Scope scope = new DefaultScope(new ProvidedInterface[0]);
+
+ assertNull(provider.getUniqueId());
+ scope.publishInterface(provider, 100);
+ assertNotNull(provider.getUniqueId());
+ }
+
+ public void testNestedLookup() {
+ ProvidedInterface provider1 = new DefaultProvidedInterface("x", Integer.class);
+ Scope parent = new DefaultScope(new ProvidedInterface[0]);
+
+ parent.publishInterface(provider1, 100);
+
+ ProvidedInterface provider2 = new DefaultProvidedInterface("y", String.class);
+ Scope child = new DefaultScope(new ProvidedInterface[0], parent);
+
+ child.publishInterface(provider2, "hallo");
+
+ assertNotNull(provider1.getUniqueId());
+ assertNotNull(provider2.getUniqueId());
+
+ assertFalse(provider1.getUniqueId().equals(provider2.getUniqueId()));
+
+ assertEquals(100, child.getInterfaceImplementation(provider1, Integer.class).intValue());
+ assertEquals("hallo", child.getInterfaceImplementation(provider2, String.class));
+ }
+
+}
--- /dev/null
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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 javax.sql.DataSource;
+
+import org.wamblee.system.core.AbstractComponent;
+import org.wamblee.system.core.DefaultProvidedInterface;
+import org.wamblee.system.core.ProvidedInterface;
+import org.wamblee.system.core.RequiredInterface;
+import org.wamblee.test.EventTracker;
+
+public class IntegerComponent extends AbstractComponent {
+
+ private static final ProvidedInterface[] provided(String aPrefix) {
+ return new ProvidedInterface[] {
+ new DefaultProvidedInterface(aPrefix + "integer", Integer.class) };
+ }
+
+ private EventTracker<String> _tracker;
+ private double _random;
+
+ public IntegerComponent() {
+ this("environment");
+ }
+
+ public IntegerComponent(String aName) {
+ this(aName, "");
+ }
+
+ public IntegerComponent(String aName, String aPrefix) {
+ super(aName, provided(aPrefix), new RequiredInterface[0]);
+ _random = Math.random();
+ }
+
+
+
+ public IntegerComponent(EventTracker aTracker) {
+ this();
+ _tracker = aTracker;
+ }
+
+ public Integer getInteger() {
+ return 2;
+ }
+
+ @Override
+ protected Object doStart(Scope aScope) {
+ addInterface(getProvidedInterfaces()[1], getInteger(), aScope);
+ track("start." + getName());
+ return _random;
+ }
+
+ @Override
+ protected void doStop(Object aRuntime) {
+ track("stop." + getName());
+ if (_random != (Double) aRuntime) {
+ throw new IllegalArgumentException("Wrong runtime: expected "
+ + _random + " but got " + aRuntime);
+ }
+ }
+
+ private void track(String aString) {
+ if (_tracker == null) {
+ return;
+ }
+ _tracker.eventOccurred(aString);
+ }
+}
--- /dev/null
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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 javax.sql.DataSource;
+
+import org.wamblee.system.core.AbstractComponent;
+import org.wamblee.system.core.DefaultProvidedInterface;
+import org.wamblee.system.core.ProvidedInterface;
+import org.wamblee.system.core.RequiredInterface;
+import org.wamblee.test.EventTracker;
+
+public class StringComponent extends AbstractComponent {
+
+ private static final ProvidedInterface[] provided(String aPrefix) {
+ return new ProvidedInterface[] {
+ new DefaultProvidedInterface(aPrefix + "datasource", String.class) };
+ }
+
+ private EventTracker<String> _tracker;
+ private double _random;
+
+ public StringComponent() {
+ this("environment");
+ }
+
+ public StringComponent(String aName) {
+ this(aName, "");
+ }
+
+ public StringComponent(String aName, String aPrefix) {
+ super(aName, provided(aPrefix), new RequiredInterface[0]);
+ _random = Math.random();
+ }
+
+
+
+ public StringComponent(EventTracker aTracker) {
+ this();
+ _tracker = aTracker;
+ }
+
+ public Integer getInteger() {
+ return 2;
+ }
+
+ public String getString() {
+ return getName() + ".hello";
+ }
+
+ @Override
+ protected Object doStart(Scope aScope) {
+ addInterface(getProvidedInterfaces()[0], getString(), aScope);
+ track("start." + getName());
+ return _random;
+ }
+
+ @Override
+ protected void doStop(Object aRuntime) {
+ track("stop." + getName());
+ if (_random != (Double) aRuntime) {
+ throw new IllegalArgumentException("Wrong runtime: expected "
+ + _random + " but got " + aRuntime);
+ }
+ }
+
+ private void track(String aString) {
+ if (_tracker == null) {
+ return;
+ }
+ _tracker.eventOccurred(aString);
+ }
+}