Title: [934] trunk: JBEHAVE-133: Added lightweight container facade - and a Pico-based implementation (others to follow suit).
Revision
934
Author
mauro
Date
2008-09-21 04:36:39 -0500 (Sun, 21 Sep 2008)

Log Message

JBEHAVE-133: Added lightweight container facade - and a Pico-based implementation (others to follow suit).
Added ContainerSteps abstract class to allow Steps to access container components.

Modified Paths

Added Paths

Diff

Modified: trunk/jbehave-core/pom.xml (933 => 934)

--- trunk/jbehave-core/pom.xml	2008-09-21 08:27:55 UTC (rev 933)
+++ trunk/jbehave-core/pom.xml	2008-09-21 09:36:39 UTC (rev 934)
@@ -1,4 +1,5 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
     <groupId>org.jbehave</groupId>
@@ -15,6 +16,10 @@
       <groupId>org.apache.ant</groupId>
       <artifactId>ant</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.picocontainer.script</groupId>
+      <artifactId>picocontainer-script-core</artifactId>
+    </dependency>
   </dependencies>
 
 </project>

Added: trunk/jbehave-core/src/behaviour/org/jbehave/container/AComponent.java (0 => 934)

--- trunk/jbehave-core/src/behaviour/org/jbehave/container/AComponent.java	                        (rev 0)
+++ trunk/jbehave-core/src/behaviour/org/jbehave/container/AComponent.java	2008-09-21 09:36:39 UTC (rev 934)
@@ -0,0 +1,5 @@
+package org.jbehave.container;
+
+public class AComponent {
+
+}

Added: trunk/jbehave-core/src/behaviour/org/jbehave/container/AnotherComponent.java (0 => 934)

--- trunk/jbehave-core/src/behaviour/org/jbehave/container/AnotherComponent.java	                        (rev 0)
+++ trunk/jbehave-core/src/behaviour/org/jbehave/container/AnotherComponent.java	2008-09-21 09:36:39 UTC (rev 934)
@@ -0,0 +1,5 @@
+package org.jbehave.container;
+
+public class AnotherComponent {
+
+}

Added: trunk/jbehave-core/src/behaviour/org/jbehave/container/pico/XMLPicoContainerBehaviour.java (0 => 934)

--- trunk/jbehave-core/src/behaviour/org/jbehave/container/pico/XMLPicoContainerBehaviour.java	                        (rev 0)
+++ trunk/jbehave-core/src/behaviour/org/jbehave/container/pico/XMLPicoContainerBehaviour.java	2008-09-21 09:36:39 UTC (rev 934)
@@ -0,0 +1,74 @@
+package org.jbehave.container.pico;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.NoSuchElementException;
+
+import org.jbehave.container.AComponent;
+import org.jbehave.container.AnotherComponent;
+import org.jbehave.container.ComponentNotFoundException;
+import org.jbehave.container.Container;
+import org.junit.Test;
+
+/**
+ * @author Mauro Talevi
+ */
+public class XMLPicoContainerBehaviour {
+
+    @Test
+    public void canGetComponentByKey() {
+        Container container = new XMLPicoContainer("org/jbehave/container/pico/components.xml");
+        assertNotNull(container.getComponent(AComponent.class, "a-component"));
+    }
+
+    @Test(expected = ComponentNotFoundException.class)
+    public void cannotGetComponentByInexistentKey() {
+        Container container = new XMLPicoContainer("org/jbehave/container/pico/components.xml");
+        container.getComponent(AComponent.class, "inexistent-key");
+    }
+
+    @Test
+    public void canGetComponentByType() {
+        Container container = new XMLPicoContainer("org/jbehave/container/pico/components.xml");
+        assertNotNull(container.getComponent(AnotherComponent.class));
+    }
+
+    @Test
+    public void canGetComponentsWithCustomClassLoader() {
+        Container container = new XMLPicoContainer("org/jbehave/container/pico/components.xml", Thread
+                .currentThread().getContextClassLoader());
+        assertNotNull(container.getComponent(AComponent.class));
+        assertNotNull(container.getComponent(AnotherComponent.class));
+    }
+
+    @Test(expected = NoSuchElementException.class)
+    public void cannotGetComponentsWithInvalidClassLoader() throws MalformedURLException {
+        new XMLPicoContainer("org/jbehave/container/pico/components.xml", new InvalidClassLoader());
+    }
+
+    @Test(expected = ComponentNotFoundException.class)
+    public void cannotGetComponentWithNoneConfigured() {
+        Container container = new XMLPicoContainer("org/jbehave/container/pico/no-components.xml");
+        container.getComponent(AComponent.class);
+    }
+
+    @Test(expected = NoSuchElementException.class)
+    public void cannotGetResourceWhenNotFound() {
+        new XMLPicoContainer("inexistent-resource.xml");
+    }
+
+    class InvalidClassLoader extends URLClassLoader {
+        public InvalidClassLoader() throws MalformedURLException {
+            super(new URL[] {});
+        }
+
+        public InputStream getResourceAsStream(String resource) {
+            return null;
+        }
+    }
+
+}

Added: trunk/jbehave-core/src/behaviour/org/jbehave/container/pico/XMLPicoContainerStepsBehaviour.java (0 => 934)

--- trunk/jbehave-core/src/behaviour/org/jbehave/container/pico/XMLPicoContainerStepsBehaviour.java	                        (rev 0)
+++ trunk/jbehave-core/src/behaviour/org/jbehave/container/pico/XMLPicoContainerStepsBehaviour.java	2008-09-21 09:36:39 UTC (rev 934)
@@ -0,0 +1,54 @@
+package org.jbehave.container.pico;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.jbehave.util.JUnit4Ensure.ensureThat;
+
+import org.jbehave.container.AComponent;
+import org.jbehave.container.AnotherComponent;
+import org.jbehave.scenario.steps.CandidateStep;
+import org.junit.Test;
+
+public class XMLPicoContainerStepsBehaviour {
+
+    @Test
+    public void shouldProvideCandidateStepsThatAreContainerAware() {
+        MySteps steps = new MySteps("org/jbehave/container/pico/components.xml");
+        CandidateStep[] candidateSteps = steps.getSteps();
+        ensureThat(candidateSteps.length, equalTo(3));
+        
+        candidateSteps[0].createFrom("Given a component").perform();
+        candidateSteps[1].createFrom("When another component").perform();
+        candidateSteps[2].createFrom("Then say hurray").perform();
+
+        ensureThat(steps.aComponent != null);
+        ensureThat(steps.anotherComponent != null);
+        ensureThat(steps.sayHurray);
+        
+    }
+
+    public static class MySteps extends XMLPicoContainerSteps {
+        
+        public boolean sayHurray;
+        public Object aComponent;
+        public Object anotherComponent;
+
+        public MySteps(String containerResource) {
+            super(containerResource);
+        }
+
+        @org.jbehave.scenario.annotations.Given("a component")
+        public void given() {
+            aComponent = component(AComponent.class);
+        }
+
+        @org.jbehave.scenario.annotations.When("another component")
+        public void when() {
+            anotherComponent = component(AnotherComponent.class);
+        }
+
+        @org.jbehave.scenario.annotations.Then("say hurray")
+        public void then() {
+            sayHurray = true;
+        }
+    }
+}

Added: trunk/jbehave-core/src/behaviour/org/jbehave/container/pico/components.xml (0 => 934)

--- trunk/jbehave-core/src/behaviour/org/jbehave/container/pico/components.xml	                        (rev 0)
+++ trunk/jbehave-core/src/behaviour/org/jbehave/container/pico/components.xml	2008-09-21 09:36:39 UTC (rev 934)
@@ -0,0 +1,10 @@
+<container>
+		
+	<component-implementation key='a-component'
+					class='org.jbehave.container.AComponent'>
+	</component-implementation>
+	
+	<component-implementation class='org.jbehave.container.AnotherComponent'>
+	</component-implementation>
+		
+</container>
\ No newline at end of file

Added: trunk/jbehave-core/src/behaviour/org/jbehave/container/pico/no-components.xml (0 => 934)

--- trunk/jbehave-core/src/behaviour/org/jbehave/container/pico/no-components.xml	                        (rev 0)
+++ trunk/jbehave-core/src/behaviour/org/jbehave/container/pico/no-components.xml	2008-09-21 09:36:39 UTC (rev 934)
@@ -0,0 +1,3 @@
+<container>
+	
+</container>
\ No newline at end of file

Added: trunk/jbehave-core/src/java/org/jbehave/container/ComponentNotFoundException.java (0 => 934)

--- trunk/jbehave-core/src/java/org/jbehave/container/ComponentNotFoundException.java	                        (rev 0)
+++ trunk/jbehave-core/src/java/org/jbehave/container/ComponentNotFoundException.java	2008-09-21 09:36:39 UTC (rev 934)
@@ -0,0 +1,15 @@
+package org.jbehave.container;
+
+/**
+ * Thrown when no component is found for a given key or type in the container
+ * 
+ * @author Mauro Talevi
+ */
[EMAIL PROTECTED]("serial")
+public class ComponentNotFoundException extends RuntimeException {
+
+    public ComponentNotFoundException(String message) {
+        super(message);
+    }
+
+}

Added: trunk/jbehave-core/src/java/org/jbehave/container/Container.java (0 => 934)

--- trunk/jbehave-core/src/java/org/jbehave/container/Container.java	                        (rev 0)
+++ trunk/jbehave-core/src/java/org/jbehave/container/Container.java	2008-09-21 09:36:39 UTC (rev 934)
@@ -0,0 +1,32 @@
+package org.jbehave.container;
+
+/**
+ * Container represents a simple facade to access components from lightweight
+ * containers. Different implementations will provide adapters to different
+ * containers.
+ * 
+ * @author Mauro Talevi
+ */
+public interface Container {
+
+    /**
+     * Returns a component of a given type
+     * 
+     * @param type the component Class type
+     * @return The component instance of type <T>
+     * @throws ComponentNotFoundException when component not found
+     */
+    <T> T getComponent(Class<T> type);
+
+    /**
+     * Returns a component for a given type and key. It first looks up the
+     * components by type and then from these the one with the provided key.
+     * 
+     * @param type the component Class type
+     * @param key the component Object key
+     * @return The component instance of type <T>
+     * @throws ComponentNotFoundException when component not found
+     */
+    <T> T getComponent(Class<T> type, Object key);
+
+}

Added: trunk/jbehave-core/src/java/org/jbehave/container/ContainerSteps.java (0 => 934)

--- trunk/jbehave-core/src/java/org/jbehave/container/ContainerSteps.java	                        (rev 0)
+++ trunk/jbehave-core/src/java/org/jbehave/container/ContainerSteps.java	2008-09-21 09:36:39 UTC (rev 934)
@@ -0,0 +1,39 @@
+package org.jbehave.container;
+
+import org.jbehave.container.pico.XMLPicoContainerSteps;
+import org.jbehave.scenario.steps.Steps;
+
+/**
+ * <p>
+ * Abstract steps decorator which adds container support. Users need to extend
+ * this class and provide a concrete implementation of Container.
+ * </p>
+ * <p>
+ * Concrete implementations provided are [EMAIL PROTECTED] XMLPicoContainerSteps}.
+ * </p>
+ * 
+ * @author Mauro Talevi
+ */
+public abstract class ContainerSteps extends Steps {
+
+    private Container container;
+
+    public ContainerSteps(String containerResource) {
+        this(containerResource, Thread.currentThread().getContextClassLoader());
+    }
+
+    public ContainerSteps(String containerResource, ClassLoader classLoader) {    
+        container = createContainer(containerResource, classLoader);
+    }
+
+    public <T> T component(Class<T> type) {
+        return container.getComponent(type);
+    }
+
+    public <T> T getComponent(Class<T> type, Object key) {
+        return container.getComponent(type, key);
+    }
+
+    protected abstract Container createContainer(String containerResource, ClassLoader classLoader);
+
+}

Added: trunk/jbehave-core/src/java/org/jbehave/container/pico/AbstractPicoContainer.java (0 => 934)

--- trunk/jbehave-core/src/java/org/jbehave/container/pico/AbstractPicoContainer.java	                        (rev 0)
+++ trunk/jbehave-core/src/java/org/jbehave/container/pico/AbstractPicoContainer.java	2008-09-21 09:36:39 UTC (rev 934)
@@ -0,0 +1,121 @@
+package org.jbehave.container.pico;
+
+import static java.text.MessageFormat.format;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import org.jbehave.container.ComponentNotFoundException;
+import org.jbehave.container.Container;
+import org.picocontainer.ComponentAdapter;
+import org.picocontainer.PicoContainer;
+import org.picocontainer.script.ScriptedContainerBuilder;
+
+/**
+ * <p>
+ * Abstract implementation of Container which uses a PicoContainer as delegate
+ * container.
+ * </p>
+ * 
+ * @author Mauro Talevi
+ */
+public abstract class AbstractPicoContainer implements Container {
+
+    private final ClassLoader classLoader;
+    private final PicoContainer container;
+
+    protected AbstractPicoContainer(String resource) {
+        this(resource, Thread.currentThread().getContextClassLoader());
+    }
+
+    protected AbstractPicoContainer(String resource, ClassLoader classLoader) {
+        this.classLoader = classLoader;
+        this.container = buildContainer(resource);
+    }
+
+    public <T> T getComponent(Class<T> type) {
+        return getPicoComponent(type, null);
+    }
+
+    public <T> T getComponent(Class<T> type, Object key) {
+        return getPicoComponent(type, key);
+    }
+
+    public <T> Collection<Object> getComponentKeys(Class<T> type) {
+        List<ComponentAdapter<T>> adapters = container.getComponentAdapters(type);
+        List<Object> keys = new ArrayList<Object>();
+        for (ComponentAdapter<T> adapter : adapters) {
+            keys.add(adapter.getComponentKey());
+        }
+        return keys;
+    }
+
+    /**
+     * Returns an instance of a component of a given type and key from the
+     * delegate PicoContainer.
+     * 
+     * @param type the component Class
+     * @param key the component key
+     * @return A component instance for the given type and key, if provided
+     * @throws NoComponentOfTypeException if no Component can be found of type
+     * @throws ComponentNotFoundException if Component not found for key
+     */
+    private <T> T getPicoComponent(Class<T> type, Object key) {
+        List<ComponentAdapter<T>> adapters = container.getComponentAdapters(type);
+        if (adapters.isEmpty()) {
+            String message = format("No component registered in container of type {0}", type);
+            throw new ComponentNotFoundException(message);
+        }
+        if (key != null) {
+            // a key has been provided: return the component for that key
+            for (ComponentAdapter<T> adapter : adapters) {
+                if (key.equals(adapter.getComponentKey())) {
+                    return adapter.getComponentInstance(container, type);
+                }
+            }
+            String message = format("No component registered in container of type {0} and for key {1}", type, key);
+            throw new ComponentNotFoundException(message);
+        } else {
+            // no key has been found:
+            // return first of registered components
+            return adapters.iterator().next().getComponentInstance(container, type);
+        }
+    }
+
+    /**
+     * Builds PicoContainer from a given resource
+     * 
+     * @param resource the String encoding the script path
+     * @return A PicoContainer
+     */
+    private PicoContainer buildContainer(String resource) {
+        Reader script = getReader(resource, classLoader);
+        ScriptedContainerBuilder builder = createContainerBuilder(script, classLoader);
+        return builder.buildContainer(null, null, true);
+    }
+
+    private Reader getReader(String resource, ClassLoader classLoader) {
+        InputStream is = classLoader.getResourceAsStream(resource);
+        if (is == null) {
+            String message = format("Resource {0} not found in ClassLoader {1}", resource, classLoader.getClass(),
+                    classLoader);
+            throw new NoSuchElementException(message);
+        }
+        return new InputStreamReader(is);
+    }
+
+    /**
+     * Allow concrete implementations to specify a ScriptedContainerBuilder
+     * 
+     * @param script the Reader containing the container script
+     * @param classLoader the ClassLoader
+     * @return A ScriptedContainerBuilder
+     */
+    protected abstract ScriptedContainerBuilder createContainerBuilder(Reader script, ClassLoader classLoader);
+
+}

Added: trunk/jbehave-core/src/java/org/jbehave/container/pico/XMLPicoContainer.java (0 => 934)

--- trunk/jbehave-core/src/java/org/jbehave/container/pico/XMLPicoContainer.java	                        (rev 0)
+++ trunk/jbehave-core/src/java/org/jbehave/container/pico/XMLPicoContainer.java	2008-09-21 09:36:39 UTC (rev 934)
@@ -0,0 +1,27 @@
+package org.jbehave.container.pico;
+
+import java.io.Reader;
+
+import org.picocontainer.script.ScriptedContainerBuilder;
+import org.picocontainer.script.xml.XMLContainerBuilder;
+
+/**
+ * PicoContainer which uses XMLContainerBuilder to build the delegate container
+ * 
+ * @author Mauro Talevi
+ */
+public class XMLPicoContainer extends AbstractPicoContainer {
+
+    public XMLPicoContainer(String resource) {
+        super(resource);
+    }
+
+    public XMLPicoContainer(String resource, ClassLoader classLoader) {
+        super(resource, classLoader);
+    }
+
+    protected ScriptedContainerBuilder createContainerBuilder(Reader script, ClassLoader classLoader) {
+        return new XMLContainerBuilder(script, classLoader);
+    }
+
+}

Added: trunk/jbehave-core/src/java/org/jbehave/container/pico/XMLPicoContainerSteps.java (0 => 934)

--- trunk/jbehave-core/src/java/org/jbehave/container/pico/XMLPicoContainerSteps.java	                        (rev 0)
+++ trunk/jbehave-core/src/java/org/jbehave/container/pico/XMLPicoContainerSteps.java	2008-09-21 09:36:39 UTC (rev 934)
@@ -0,0 +1,25 @@
+package org.jbehave.container.pico;
+
+import org.jbehave.container.Container;
+import org.jbehave.container.ContainerSteps;
+
+/**
+ * XMLPicoContainer-based Steps decorator.
+ * 
+ * @author Mauro Talevi
+ */
+public class XMLPicoContainerSteps extends ContainerSteps {
+
+    public XMLPicoContainerSteps(String containerResource) {
+        super(containerResource);
+    }
+
+    public XMLPicoContainerSteps(String containerResource, ClassLoader classLoader) {
+        super(containerResource, classLoader);
+    }
+
+    protected Container createContainer(String containerResource, ClassLoader classLoader) {
+        return new XMLPicoContainer(containerResource, classLoader);
+    }
+
+}

Modified: trunk/pom.xml (933 => 934)

--- trunk/pom.xml	2008-09-21 08:27:55 UTC (rev 933)
+++ trunk/pom.xml	2008-09-21 09:36:39 UTC (rev 934)
@@ -1,4 +1,5 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>org.jbehave</groupId>
   <artifactId>jbehave</artifactId>
@@ -30,6 +31,15 @@
         <scope>provided</scope>
       </dependency>
       <dependency>
+        <!-- Optional as only used by ContainerSteps -->
+        <groupId>org.picocontainer.script</groupId>
+        <artifactId>picocontainer-script-core</artifactId>
+        <version>2.0</version>
+        <optional>true</optional>
+        <scope>provided</scope>
+      </dependency>
+
+      <dependency>
         <groupId>org.mockito</groupId>
         <artifactId>mockito-all</artifactId>
         <version>1.2</version>


To unsubscribe from this list please visit:

http://xircles.codehaus.org/manage_email

Reply via email to