Commit the event admin handler test suite
Fix the whiteboard pattern handler test suite group id

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@681155 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/tests/handler/eventadmin/pom.xml b/ipojo/tests/handler/eventadmin/pom.xml
new file mode 100644
index 0000000..06291aa
--- /dev/null
+++ b/ipojo/tests/handler/eventadmin/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<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">

+	<groupId>ipojo.tests</groupId>

+	<version>0.9.0-SNAPSHOT</version>

+	<modelVersion>4.0.0</modelVersion>

+	<packaging>bundle</packaging>

+	<name>iPOJO Event Admin Handler Test Suite</name>

+	<artifactId>tests.eventadmin.handler</artifactId>

+	<dependencies>

+		<dependency>

+			<groupId>org.apache.felix</groupId>

+			<artifactId>org.apache.felix.ipojo</artifactId>

+			<version>0.9.0-SNAPSHOT</version>

+		</dependency>

+		<dependency>

+			<groupId>org.apache.felix</groupId>

+			<artifactId>org.osgi.core</artifactId>

+			<version>1.0.0</version>

+		</dependency>

+		<dependency>

+			<groupId>org.apache.felix</groupId>

+			<artifactId>org.apache.felix.ipojo.handler.eventadmins</artifactId>

+			<version>0.9.0-SNAPSHOT</version>

+		</dependency>

+		<dependency>

+			<groupId>junit</groupId>

+			<artifactId>junit</artifactId>

+			<version>3.8.1</version>

+		</dependency>

+		<dependency>

+			<groupId>ipojo.examples</groupId>

+			<artifactId>org.apache.felix.ipojo.junit4osgi</artifactId>

+			<version>0.9.0-SNAPSHOT</version>

+		</dependency>

+	</dependencies>

+	<build>

+		<plugins>

+			<plugin>

+				<groupId>org.apache.felix</groupId>

+				<artifactId>maven-bundle-plugin</artifactId>

+				<version>1.4.0</version>

+				<extensions>true</extensions>

+				<configuration>

+					<instructions>

+						<Private-Package>org.apache.felix.ipojo.test.*

+							</Private-Package>

+						<Test-Suite>

+							org.apache.felix.ipojo.test.EahTestSuite

+						</Test-Suite>

+					</instructions>

+				</configuration>

+			</plugin>

+			<plugin>

+				<groupId>org.apache.felix</groupId>

+				<artifactId>maven-ipojo-plugin</artifactId>

+				<version>0.9.0-SNAPSHOT</version>

+				<executions>

+					<execution>

+						<goals>

+							<goal>ipojo-bundle</goal>

+						</goals>

+						<configuration>

+							<ignoreAnnotations>true</ignoreAnnotations>

+						</configuration>

+					</execution>

+				</executions>

+			</plugin>

+		</plugins>

+	</build>

+</project>

diff --git a/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/BadTests.java b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/BadTests.java
new file mode 100644
index 0000000..797398e
--- /dev/null
+++ b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/BadTests.java
@@ -0,0 +1,875 @@
+/* 

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.apache.felix.ipojo.test;

+

+import java.util.Dictionary;

+import java.util.Hashtable;

+

+import org.apache.felix.ipojo.ComponentFactory;

+import org.apache.felix.ipojo.ComponentInstance;

+import org.apache.felix.ipojo.ConfigurationException;

+import org.apache.felix.ipojo.MissingHandlerException;

+import org.apache.felix.ipojo.UnacceptableConfiguration;

+import org.apache.felix.ipojo.junit4osgi.OSGiTestCase;

+import org.apache.felix.ipojo.metadata.Attribute;

+import org.apache.felix.ipojo.metadata.Element;

+import org.apache.felix.ipojo.parser.ManifestMetadataParser;

+import org.apache.felix.ipojo.parser.ParseException;

+import org.apache.felix.ipojo.test.donut.Donut;

+import org.apache.felix.ipojo.test.donut.DonutConsumer;

+import org.apache.felix.ipojo.test.donut.DonutProvider;

+import org.apache.felix.ipojo.test.util.IPojoTestUtils;

+import org.osgi.framework.ServiceReference;

+

+/**

+ * Test the good behaviour of the EventAdminHandler.

+ * 

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class BadTests extends OSGiTestCase {

+

+    /**

+     * The namespace of the Event admin handler.

+     */

+    private static final String NAMESPACE = "org.apache.felix.ipojo.handlers.event.EventAdminHandler";

+

+    /**

+     * The available components.

+     */

+    private Element[] m_components;

+

+    /**

+     * The description of a component that uses an event publisher.

+     */

+    private Element m_provider;

+

+    /**

+     * The event publisher description.

+     */

+    private Element m_publisher;

+

+    /**

+     * The name attribute of the event publisher.

+     */

+    private Attribute m_publisherName;

+

+    /**

+     * The field attribute of the event publisher.

+     */

+    private Attribute m_publisherField;

+

+    /**

+     * The topics attribute of the event publisher.

+     */

+    private Attribute m_publisherTopics;

+

+    /**

+     * The data-key attribute of the event publisher.

+     */

+    private Attribute m_publisherDataKey;

+

+    /**

+     * The synchronous attribute of the event publisher.

+     */

+    private Attribute m_publisherSynchronous;

+

+    /**

+     * The description of a component that uses an event subscriber.

+     */

+    private Element m_consumer;

+

+    /**

+     * The event subscriber description.

+     */

+    private Element m_subscriber;

+

+    /**

+     * The name attribute of the event subscriber.

+     */

+    private Attribute m_subscriberName;

+

+    /**

+     * The callback attribute of the event subscriber.

+     */

+    private Attribute m_subscriberCallback;

+

+    /**

+     * The topics attribute of the event subscriber.

+     */

+    private Attribute m_subscriberTopics;

+

+    /**

+     * The data-key attribute of the event subscriber.

+     */

+    private Attribute m_subscriberDataKey;

+

+    /**

+     * The data-type attribute of the event subscriber.

+     */

+    private Attribute m_subscriberDataType;

+

+    private Element getManipulationForComponent(String compName) {

+        for (int i = 0; i < m_components.length; i++) {

+            if (m_components[i].containsAttribute("name")

+                    && m_components[i].getAttribute("name").equals(compName)) {

+                return m_components[i].getElements("manipulation")[0];

+            }

+        }

+        return null;

+    }

+

+    /**

+     * Initialization before test cases.

+     * 

+     * Create all the instances

+     * 

+     */

+    public void setUp() {

+

+        /**

+         * Get the list of available components.

+         */

+        try {

+            String header = (String) context.getBundle().getHeaders().get(

+                    "iPOJO-Components");

+            m_components = ManifestMetadataParser.parseHeaderMetadata(header)

+                    .getElements("component");

+        } catch (ParseException e) {

+            fail("Parse Exception when parsing iPOJO-Component");

+        }

+

+        /**

+         * Initialize the standard publishing component (based on the

+         * asynchronous donut provider).

+         */

+        m_provider = new Element("component", "");

+        m_provider.addAttribute(new Attribute("className",

+                "org.apache.felix.ipojo.test.donut.DonutProviderImpl"));

+        m_provider.addAttribute(new Attribute("name",

+                "standard donut provider for bad tests"));

+

+        // The provided service of the publisher

+        Element providesDonutProvider = new Element("provides", "");

+        providesDonutProvider.addAttribute(new Attribute("interface",

+                "org.apache.felix.ipojo.test.donut.DonutProvider"));

+        Element providesDonutProviderProperty = new Element("property", "");

+        providesDonutProviderProperty

+                .addAttribute(new Attribute("name", "name"));

+        providesDonutProviderProperty.addAttribute(new Attribute("field",

+                "m_name"));

+        providesDonutProviderProperty.addAttribute(new Attribute("value",

+                "Unknown donut vendor"));

+        providesDonutProvider.addElement(providesDonutProviderProperty);

+        m_provider.addElement(providesDonutProvider);

+

+        // The event publisher, corresponding to the following description :

+        // <ev:publisher name="donut-publisher" field="m_publisher"

+        // topics="food/donuts" data-key="food" synchronous="false"/>

+        m_publisher = new Element("publisher", NAMESPACE);

+        m_publisherName = new Attribute("name", "donut-publisher");

+        m_publisherField = new Attribute("field", "m_publisher");

+        m_publisherTopics = new Attribute("topics", "food/donuts");

+        m_publisherDataKey = new Attribute("data-key", "food");

+        m_publisherSynchronous = new Attribute("synchronous", "false");

+        m_publisher.addAttribute(m_publisherName);

+        m_publisher.addAttribute(m_publisherField);

+        m_publisher.addAttribute(m_publisherTopics);

+        m_publisher.addAttribute(m_publisherDataKey);

+        m_publisher.addAttribute(m_publisherSynchronous);

+        m_provider.addElement(m_publisher);

+

+        m_provider.addElement(getManipulationForComponent("donut-provider"));

+

+        /**

+         * Initialize the standard subscribing component (based on the donut

+         * consumer).

+         */

+        m_consumer = new Element("component", "");

+        m_consumer.addAttribute(new Attribute("className",

+                "org.apache.felix.ipojo.test.donut.DonutConsumerImpl"));

+        m_consumer.addAttribute(new Attribute("name",

+                "standard donut consumer for bad tests"));

+

+        // The provided service of the publisher

+        Element providesDonutConsumer = new Element("provides", "");

+        providesDonutConsumer.addAttribute(new Attribute("interface",

+                "org.apache.felix.ipojo.test.donut.DonutConsumer"));

+        Element providesDonutConsumerNameProperty = new Element("property", "");

+        providesDonutConsumerNameProperty.addAttribute(new Attribute("name",

+                "name"));

+        providesDonutConsumerNameProperty.addAttribute(new Attribute("field",

+                "m_name"));

+        providesDonutConsumerNameProperty.addAttribute(new Attribute("value",

+                "Unknown donut consumer"));

+        providesDonutConsumer.addElement(providesDonutConsumerNameProperty);

+        Element providesDonutConsumerSlowProperty = new Element("property", "");

+        providesDonutConsumerSlowProperty.addAttribute(new Attribute("name",

+                "slow"));

+        providesDonutConsumerSlowProperty.addAttribute(new Attribute("field",

+                "m_isSlow"));

+        providesDonutConsumerSlowProperty.addAttribute(new Attribute("value",

+                "false"));

+        providesDonutConsumer.addElement(providesDonutConsumerSlowProperty);

+        m_consumer.addElement(providesDonutConsumer);

+

+        // The event publisher, corresponding to the following description :

+        // <ev:subscriber name="donut-subscriber" callback="receiveDonut"

+        // topics="food/donuts" data-key="food"

+        // data-type="org.apache.felix.ipojo.test.donut.Donut"/>

+        m_subscriber = new Element("subscriber", NAMESPACE);

+        m_subscriberName = new Attribute("name", "donut-subscriber");

+        m_subscriberCallback = new Attribute("callback", "receiveDonut");

+        m_subscriberTopics = new Attribute("topics", "food/donuts");

+        m_subscriberDataKey = new Attribute("data-key", "food");

+        m_subscriberDataType = new Attribute("data-type",

+                "org.apache.felix.ipojo.test.donut.Donut");

+        m_subscriber.addAttribute(m_subscriberName);

+        m_subscriber.addAttribute(m_subscriberCallback);

+        m_subscriber.addAttribute(m_subscriberTopics);

+        m_subscriber.addAttribute(m_subscriberDataKey);

+        m_subscriber.addAttribute(m_subscriberDataType);

+        m_consumer.addElement(m_subscriber);

+

+        m_consumer.addElement(getManipulationForComponent("donut-consumer"));

+    }

+

+    /**

+     * Test the base configuration is correct to be sure the bad tests will fail

+     * because of they are really bad, and not because of an other application

+     * error.

+     * 

+     * This test simply create a provider and a consumer instance, send one

+     * event and check it is received.

+     * 

+     * @throws ConfigurationException

+     *             something bad happened

+     * @throws MissingHandlerException

+     *             something bad happened

+     * @throws UnacceptableConfiguration

+     *             something bad happened

+     */

+    public void testGoodConfig()

+        throws ConfigurationException, UnacceptableConfiguration,

+        MissingHandlerException {

+        /**

+         * Create the provider and the consumer instances.

+         */

+        Dictionary properties = new Hashtable();

+

+        // Provider

+        ComponentFactory providerFactory = new ComponentFactory(context,

+                m_provider);

+        providerFactory.start();

+        properties.put("name", "Emperor of donuts");

+        ComponentInstance providerInstance = providerFactory

+                .createComponentInstance(properties);

+        ServiceReference providerService = IPojoTestUtils

+                .getServiceReferenceByName(context, DonutProvider.class

+                        .getName(), providerInstance.getInstanceName());

+        DonutProvider provider = (DonutProvider) context

+                .getService(providerService);

+

+        // The consumer

+        ComponentFactory consumerFactory = new ComponentFactory(context,

+                m_consumer);

+        consumerFactory.start();

+        properties.put("name", "Homer Simpson");

+        properties.put("slow", "false");

+        ComponentInstance consumerInstance = consumerFactory

+                .createComponentInstance(properties);

+        ServiceReference consumerService = IPojoTestUtils

+                .getServiceReferenceByName(context, DonutConsumer.class

+                        .getName(), consumerInstance.getInstanceName());

+        DonutConsumer consumer = (DonutConsumer) context

+                .getService(consumerService);

+

+        /**

+         * Test the normal behaviour of the instances.

+         */

+        consumer.clearDonuts();

+        Donut sentDonut = provider.sellDonut();

+        Donut receivedDonut = consumer.waitForDonut();

+        assertEquals("The received donut must be the same as the sent one.",

+                sentDonut, receivedDonut);

+

+        /**

+         * Destroy component's instances.

+         */

+        context.ungetService(providerService);

+        providerInstance.dispose();

+        context.ungetService(consumerService);

+        consumerInstance.dispose();

+        providerFactory.stop();

+        consumerFactory.stop();

+    }

+

+    /**

+     * Try to create a publisher with no name.

+     * 

+     * @throws ConfigurationException

+     *             something bad happened

+     * @throws MissingHandlerException

+     *             something bad happened

+     * @throws UnacceptableConfiguration

+     *             something bad happened

+     */

+    public void testPublisherWithoutName()

+        throws ConfigurationException, MissingHandlerException,

+        UnacceptableConfiguration {

+

+        // Remove the name attribute of the publisher

+        m_publisher.removeAttribute(m_publisherName);

+

+        // Create and try to start the factory

+        ComponentFactory fact = new ComponentFactory(context, m_provider);

+        try {

+            fact.start();

+            // Should not be executed

+            fact.stop();

+            fail("The factory must not start when no name is specified.");

+        } catch (IllegalStateException e) {

+            // OK

+        } finally {

+            // Restore the original state of the publisher

+            m_publisher.addAttribute(m_publisherName);

+        }

+    }

+

+    /**

+     * Try to create a publisher with no field.

+     * 

+     * @throws ConfigurationException

+     *             something bad happened

+     * @throws MissingHandlerException

+     *             something bad happened

+     * @throws UnacceptableConfiguration

+     *             something bad happened

+     */

+    public void testPublisherWithoutField()

+        throws ConfigurationException, MissingHandlerException,

+        UnacceptableConfiguration {

+

+        // Remove the name attribute of the publisher

+        m_publisher.removeAttribute(m_publisherField);

+

+        // Create and try to start the factory

+        ComponentFactory fact = new ComponentFactory(context, m_provider);

+        try {

+            fact.start();

+            // Should not be executed

+            fact.stop();

+            fail("The factory must not start when no field is specified.");

+        } catch (IllegalStateException e) {

+            // OK

+        } finally {

+            // Restore the original state of the publisher

+            m_publisher.addAttribute(m_publisherField);

+        }

+    }

+

+    /**

+     * Try to create a publisher with an unexisting field.

+     * 

+     * @throws ConfigurationException

+     *             something bad happened

+     * @throws MissingHandlerException

+     *             something bad happened

+     * @throws UnacceptableConfiguration

+     *             something bad happened

+     */

+    public void testPublisherWithUnexistingField()

+        throws ConfigurationException, MissingHandlerException,

+        UnacceptableConfiguration {

+

+        // Remove the name attribute of the publisher and replace with an

+        // unexisting field name

+        m_publisher.removeAttribute(m_publisherField);

+        Attribute unexistingField = new Attribute("field", "m_unexistingField");

+        m_publisher.addAttribute(unexistingField);

+

+        // Create and try to start the factory

+        ComponentFactory fact = new ComponentFactory(context, m_provider);

+        try {

+            fact.start();

+            // Should not be executed

+            fact.stop();

+            fail("The factory must not start when an unexisting field is specified.");

+        } catch (IllegalStateException e) {

+            // OK

+        } finally {

+            // Restore the original state of the publisher

+            m_publisher.removeAttribute(unexistingField);

+            m_publisher.addAttribute(m_publisherField);

+        }

+    }

+

+    /**

+     * Try to create a publisher with a bad typed field.

+     * 

+     * @throws ConfigurationException

+     *             something bad happened

+     * @throws MissingHandlerException

+     *             something bad happened

+     * @throws UnacceptableConfiguration

+     *             something bad happened

+     */

+    public void testPublisherWithBadTypedField()

+        throws ConfigurationException, MissingHandlerException,

+        UnacceptableConfiguration {

+

+        // Remove the name attribute of the publisher and replace with an

+        // bad typed field name

+        m_publisher.removeAttribute(m_publisherField);

+        Attribute badTypedField = new Attribute("field", "m_name");

+        m_publisher.addAttribute(badTypedField);

+

+        // Create and try to start the factory

+        ComponentFactory fact = new ComponentFactory(context, m_provider);

+        try {

+            fact.start();

+            // Should not be executed

+            fact.stop();

+            fail("The factory must not start when an bad typed field is specified.");

+        } catch (IllegalStateException e) {

+            // OK

+        } finally {

+            // Restore the original state of the publisher

+            m_publisher.removeAttribute(badTypedField);

+            m_publisher.addAttribute(m_publisherField);

+        }

+    }

+

+    /**

+     * Try to create a publisher instance without topics.

+     * 

+     * @throws ConfigurationException

+     *             something bad happened

+     * @throws MissingHandlerException

+     *             something bad happened

+     * @throws UnacceptableConfiguration

+     *             something bad happened

+     */

+    public void testPublisherWithoutTopics()

+        throws ConfigurationException, MissingHandlerException,

+        UnacceptableConfiguration {

+

+        // Remove the topics attribute of the publisher

+        m_publisher.removeAttribute(m_publisherTopics);

+        ComponentFactory fact = new ComponentFactory(context, m_provider);

+        fact.start();

+

+        // Try to create an instance without specified topics

+        Dictionary conf = new Hashtable();

+        conf.put("name", "provider without topics");

+

+        ComponentInstance instance;

+        try {

+            instance = fact.createComponentInstance(conf);

+            // Should not be executed

+            instance.dispose();

+            fail("The factory must not create instance without specified topics.");

+        } catch (ConfigurationException e) {

+            // OK

+        } finally {

+            fact.stop();

+            // Restore the original state of the publisher

+            m_publisher.addAttribute(m_publisherTopics);

+        }

+    }

+

+    /**

+     * Try to create a publisher with malformed topics.

+     * 

+     * @throws ConfigurationException

+     *             something bad happened

+     * @throws MissingHandlerException

+     *             something bad happened

+     * @throws UnacceptableConfiguration

+     *             something bad happened

+     */

+    public void testPublisherWithMalformedTopics()

+        throws ConfigurationException, MissingHandlerException,

+        UnacceptableConfiguration {

+

+        // Remove the topics attribute of the publisher and replace with a

+        // malformed one

+        m_publisher.removeAttribute(m_publisherTopics);

+        Attribute malformedTopics = new Attribute("topics",

+                "| |\\| \\/ /-\\ |_ | |)");

+        m_publisher.addAttribute(malformedTopics);

+

+        // Create and try to start the factory

+        ComponentFactory fact = new ComponentFactory(context, m_provider);

+        try {

+            fact.start();

+            // Should not be executed

+            fact.stop();

+            fail("The factory must not start when invalid topics are specified.");

+        } catch (IllegalStateException e) {

+            // OK

+        } finally {

+            // Restore the original state of the publisher

+            m_publisher.removeAttribute(malformedTopics);

+            m_publisher.addAttribute(m_publisherTopics);

+        }

+    }

+

+    /**

+     * Try to create a publisher with malformed instance topics.

+     * 

+     * @throws ConfigurationException

+     *             something bad happened

+     * @throws MissingHandlerException

+     *             something bad happened

+     * @throws UnacceptableConfiguration

+     *             something bad happened

+     */

+    public void testPublisherWithMalformedInstanceTopics()

+        throws ConfigurationException, MissingHandlerException,

+        UnacceptableConfiguration {

+

+        // Remove the topics attribute of the publisher and replace with a

+        // malformed one

+        m_publisher.removeAttribute(m_publisherTopics);

+        ComponentFactory fact = new ComponentFactory(context, m_provider);

+        fact.start();

+

+        // Try to create an instance with malformed specified topics

+        Dictionary conf = new Hashtable();

+        conf.put("name", "provider with malformed topics");

+        Dictionary topics = new Hashtable();

+        topics.put("donut-publisher", "| |\\| \\/ /-\\ |_ | |)");

+        conf.put("event.topics", topics);

+

+        ComponentInstance instance;

+        try {

+            instance = fact.createComponentInstance(conf);

+            // Should not be executed

+            instance.dispose();

+            fail("The factory must not create instance with invalid specified topics.");

+        } catch (ConfigurationException e) {

+            // OK

+        } finally {

+            fact.stop();

+            // Restore the original state of the publisher

+            m_publisher.addAttribute(m_publisherTopics);

+        }

+    }

+

+    /**

+     * Try to create a subscriber with no name.

+     * 

+     * @throws ConfigurationException

+     *             something bad happened

+     * @throws MissingHandlerException

+     *             something bad happened

+     * @throws UnacceptableConfiguration

+     *             something bad happened

+     */

+    public void testSubscriberWithoutName()

+        throws ConfigurationException, MissingHandlerException,

+        UnacceptableConfiguration {

+

+        // Remove the name attribute of the publisher

+        m_subscriber.removeAttribute(m_subscriberName);

+

+        // Create and try to start the factory

+        ComponentFactory fact = new ComponentFactory(context, m_consumer);

+        try {

+            fact.start();

+            // Should not be executed

+            fact.stop();

+            fail("The factory must not start when no name is specified.");

+        } catch (IllegalStateException e) {

+            // OK

+        } finally {

+            // Restore the original state of the publisher

+            m_subscriber.addAttribute(m_subscriberName);

+        }

+    }

+

+    /**

+     * Try to create a subscriber with no callback.

+     * 

+     * @throws ConfigurationException

+     *             something bad happened

+     * @throws MissingHandlerException

+     *             something bad happened

+     * @throws UnacceptableConfiguration

+     *             something bad happened

+     */

+    public void testSubscriberWithoutCallback()

+        throws ConfigurationException, MissingHandlerException,

+        UnacceptableConfiguration {

+

+        // Remove the name attribute of the publisher

+        m_subscriber.removeAttribute(m_subscriberCallback);

+

+        // Create and try to start the factory

+        ComponentFactory fact = new ComponentFactory(context, m_consumer);

+        try {

+            fact.start();

+            // Should not be executed

+            fact.stop();

+            fail("The factory must not start when no callback is specified.");

+        } catch (IllegalStateException e) {

+            // OK

+        } finally {

+            // Restore the original state of the publisher

+            m_subscriber.addAttribute(m_subscriberCallback);

+        }

+    }

+

+    /**

+     * Try to create a subscriber instance without topics.

+     * 

+     * @throws ConfigurationException

+     *             something bad happened

+     * @throws MissingHandlerException

+     *             something bad happened

+     * @throws UnacceptableConfiguration

+     *             something bad happened

+     */

+    public void testSubscriberWithoutTopics()

+        throws ConfigurationException, MissingHandlerException,

+        UnacceptableConfiguration {

+

+        // Remove the topics attribute of the subscriber

+        m_subscriber.removeAttribute(m_subscriberTopics);

+        ComponentFactory fact = new ComponentFactory(context, m_consumer);

+        fact.start();

+

+        // Try to create an instance without specified topics

+        Dictionary conf = new Hashtable();

+        conf.put("name", "consumer without topics");

+        conf.put("slow", "false");

+

+        ComponentInstance instance;

+        try {

+            instance = fact.createComponentInstance(conf);

+            // Should not be executed

+            instance.dispose();

+            fail("The factory must not create instance without specified topics.");

+        } catch (ConfigurationException e) {

+            // OK

+        } finally {

+            fact.stop();

+            // Restore the original state of the subscriber

+            m_subscriber.addAttribute(m_subscriberTopics);

+        }

+    }

+

+    /**

+     * Try to create a subscriber with malformed topics.

+     * 

+     * @throws ConfigurationException

+     *             something bad happened

+     * @throws MissingHandlerException

+     *             something bad happened

+     * @throws UnacceptableConfiguration

+     *             something bad happened

+     */

+    public void testSubscriberWithMalformedTopics()

+        throws ConfigurationException, MissingHandlerException,

+        UnacceptableConfiguration {

+

+        // Remove the topics attribute of the subscriber and replace with a

+        // malformed one

+        m_subscriber.removeAttribute(m_subscriberTopics);

+        Attribute malformedTopics = new Attribute("topics",

+                "| |\\| \\/ /-\\ |_ | |)");

+        m_subscriber.addAttribute(malformedTopics);

+

+        // Create and try to start the factory

+        ComponentFactory fact = new ComponentFactory(context, m_consumer);

+        try {

+            fact.start();

+            // Should not be executed

+            fact.stop();

+            fail("The factory must not start when invalid topics are specified.");

+        } catch (IllegalStateException e) {

+            // OK

+        } finally {

+            // Restore the original state of the subscriber

+            m_subscriber.removeAttribute(malformedTopics);

+            m_subscriber.addAttribute(m_subscriberTopics);

+        }

+    }

+

+    /**

+     * Try to create a subscriber with malformed instance topics.

+     * 

+     * @throws ConfigurationException

+     *             something bad happened

+     * @throws MissingHandlerException

+     *             something bad happened

+     * @throws UnacceptableConfiguration

+     *             something bad happened

+     */

+    public void testSubscriberWithMalformedInstanceTopics()

+        throws ConfigurationException, MissingHandlerException,

+        UnacceptableConfiguration {

+

+        // Remove the topics attribute of the subscriber and replace with a

+        // malformed one

+        m_subscriber.removeAttribute(m_subscriberTopics);

+        ComponentFactory fact = new ComponentFactory(context, m_consumer);

+        fact.start();

+

+        // Try to create an instance with malformed specified topics

+        Dictionary conf = new Hashtable();

+        conf.put("name", "consumer with malformed topics");

+        Dictionary topics = new Hashtable();

+        topics.put("donut-subscriber", "| |\\| \\/ /-\\ |_ | |)");

+        conf.put("event.topics", topics);

+

+        ComponentInstance instance;

+        try {

+            instance = fact.createComponentInstance(conf);

+            // Should not be executed

+            instance.dispose();

+            fail("The factory must not create instance with invalid specified topics.");

+        } catch (ConfigurationException e) {

+            // OK

+        } finally {

+            fact.stop();

+            // Restore the original state of the subscriber

+            m_subscriber.addAttribute(m_subscriberTopics);

+        }

+    }

+

+    /**

+     * Try to create a subscriber with unknown data type.

+     * 

+     * @throws ConfigurationException

+     *             something bad happened

+     * @throws MissingHandlerException

+     *             something bad happened

+     * @throws UnacceptableConfiguration

+     *             something bad happened

+     */

+    public void testSubscriberWithUnknownDataType()

+        throws ConfigurationException, MissingHandlerException,

+        UnacceptableConfiguration {

+

+        // Remove the data-type attribute of the subscriber and replace with a

+        // malformed one

+        m_subscriber.removeAttribute(m_subscriberDataType);

+        Attribute unknownType = new Attribute("data-type", "org.unknown.Clazz");

+        m_subscriber.addAttribute(unknownType);

+

+        // Create and try to start the factory

+        ComponentFactory fact = new ComponentFactory(context, m_consumer);

+        try {

+            fact.start();

+            // Should not be executed

+            fact.stop();

+            fail("The factory must not start when unknown data type is specified.");

+        } catch (IllegalStateException e) {

+            // OK

+        } finally {

+            // Restore the original state of the subscriber

+            m_subscriber.removeAttribute(unknownType);

+            m_subscriber.addAttribute(m_subscriberDataType);

+        }

+    }

+

+    /**

+     * Try to create a subscriber with a data type that does not match with the

+     * callback parameter type.

+     * 

+     * @throws ConfigurationException

+     *             something bad happened

+     * @throws MissingHandlerException

+     *             something bad happened

+     * @throws UnacceptableConfiguration

+     *             something bad happened

+     */

+    public void testSubscriberWithUnappropriatedDataType()

+        throws ConfigurationException, MissingHandlerException,

+        UnacceptableConfiguration {

+

+        // Remove the data-type attribute of the subscriber and replace with a

+        // malformed one

+        m_subscriber.removeAttribute(m_subscriberDataType);

+        Attribute unknownType = new Attribute("data-type", "java.lang.String");

+        m_subscriber.addAttribute(unknownType);

+

+        // Create and try to start the factory

+        ComponentFactory fact = new ComponentFactory(context, m_consumer);

+        try {

+            fact.start();

+            // Should not be executed

+            fact.stop();

+            fail("The factory must not start when unappropriated data type is specified.");

+        } catch (IllegalStateException e) {

+            // OK

+        } finally {

+            // Restore the original state of the subscriber

+            m_subscriber.removeAttribute(unknownType);

+            m_subscriber.addAttribute(m_subscriberDataType);

+        }

+    }

+

+    /**

+     * Finalization after test cases.

+     * 

+     * Release all services references and destroy instances.

+     */

+    public void tearDown() {

+

+    }

+

+    // DEBUG

+    public void dumpElement(String message, Element root) {

+        System.err.println(message + "\n" + dumpElement(0, root));

+    }

+

+    // DEBUG

+    private String dumpElement(int level, Element element) {

+        StringBuilder sb = new StringBuilder();

+        // Enter tag

+        for (int i = 0; i < level; i++) {

+            sb.append("  ");

+        }

+        sb.append('<');

+        sb.append(element.getName());

+        Attribute[] attributes = element.getAttributes();

+        for (int i = 0; i < attributes.length; i++) {

+            Attribute attribute = attributes[i];

+            sb.append(' ');

+            sb.append(attribute.getName());

+            sb.append('=');

+            sb.append(attribute.getValue());

+        }

+        sb.append(">\n");

+        // Children

+        Element[] elements = element.getElements();

+        for (int i = 0; i < elements.length; i++) {

+            sb.append(dumpElement(level + 1, elements[i]));

+        }

+        // Exit tag

+        for (int i = 0; i < level; i++) {

+            sb.append("  ");

+        }

+        sb.append("</" + element.getName() + ">\n");

+        return sb.toString();

+    }

+

+}

diff --git a/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/EahTestSuite.java b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/EahTestSuite.java
new file mode 100644
index 0000000..89dd5e4
--- /dev/null
+++ b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/EahTestSuite.java
@@ -0,0 +1,48 @@
+/* 

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.apache.felix.ipojo.test;

+

+import junit.framework.Test;

+

+import org.apache.felix.ipojo.junit4osgi.OSGiTestSuite;

+import org.osgi.framework.BundleContext;

+

+/**

+ * Event Admin Handler test suite.

+ * 

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class EahTestSuite {

+

+    /**

+     * Generate the Event Admin Handler test suite.

+     * 

+     * @param bc

+     *            the OSGi bundle context

+     * @return the Event Admin Handler test suite.

+     */

+    public static Test suite(BundleContext bc) {

+        OSGiTestSuite ots = new OSGiTestSuite("Event Admin Handler test suite",

+                bc);

+        ots.addTestSuite(BadTests.class);

+        ots.addTestSuite(GoodTests.class);

+        return ots;

+    }

+

+}

diff --git a/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/GoodTests.java b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/GoodTests.java
new file mode 100644
index 0000000..1a7bec3
--- /dev/null
+++ b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/GoodTests.java
@@ -0,0 +1,716 @@
+/* 

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.apache.felix.ipojo.test;

+

+import java.util.ArrayList;

+import java.util.Arrays;

+import java.util.Dictionary;

+import java.util.Hashtable;

+import java.util.List;

+

+import org.apache.felix.ipojo.ComponentInstance;

+import org.apache.felix.ipojo.ConfigurationException;

+import org.apache.felix.ipojo.MissingHandlerException;

+import org.apache.felix.ipojo.UnacceptableConfiguration;

+import org.apache.felix.ipojo.junit4osgi.OSGiTestCase;

+import org.apache.felix.ipojo.test.donut.Donut;

+import org.apache.felix.ipojo.test.donut.DonutConsumer;

+import org.apache.felix.ipojo.test.donut.DonutProvider;

+import org.apache.felix.ipojo.test.donut.EventTracker;

+import org.apache.felix.ipojo.test.util.EahTestUtils;

+import org.apache.felix.ipojo.test.util.IPojoTestUtils;

+import org.osgi.framework.ServiceReference;

+import org.osgi.service.event.Event;

+

+/**

+ * Test the good behaviour of the EventAdminHandler.

+ * 

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class GoodTests extends OSGiTestCase {

+

+    /**

+     * The number of providers to test.

+     */

+    private static final int NUMBER_OF_PROVIDERS = 6;

+

+    /**

+     * The number of providers using the event admin handler to test.

+     */

+    private static final int NUMBER_OF_EAH_PROVIDERS = 4;

+

+    /**

+     * The number of consumers to test.

+     */

+    private static final int NUMBER_OF_CONSUMERS = 6;

+

+    /**

+     * The number of synchronous providers to test.

+     */

+    private static final int NUMBER_OF_SYNCHRONOUS_PROVIDERS = 3;

+

+    /**

+     * The number of slow consumers to test.

+     */

+    private static final int NUMBER_OF_QUICK_CONSUMERS = 3;

+

+    /**

+     * The list of topics to test.

+     */

+    private static final String[] TOPICS_LIST = { "foo", "bar", "nut",

+        "foo,bar", "bar,nut", "foo,nut", "foo,bar,nut" };

+

+    /**

+     * The utility class instance.

+     */

+    public EahTestUtils m_utils;

+

+    /**

+     * The providers' instances.

+     */

+    private ComponentInstance[] m_providersInstances;

+

+    /**

+     * The providers' service references.

+     */

+    private ServiceReference[] m_providersServices;

+

+    /**

+     * The providers' services.

+     */

+    private DonutProvider[] m_providers;

+

+    /**

+     * The synchronous providers' services.

+     */

+    private DonutProvider[] m_synchronousProviders;

+

+    /**

+     * The instances of providers that uses the event admin handler.

+     */

+    private ComponentInstance[] m_eahProvidersInstances;

+

+    /**

+     * The services of the providers that uses the event admin handler.

+     */

+    private DonutProvider[] m_eahProviders;

+

+    /**

+     * The synchronous donut event provider service.

+     */

+    private DonutProvider m_synchronousDonutEventProvider;

+

+    /**

+     * The consumers' instances.

+     */

+    private ComponentInstance[] m_consumersInstances;

+

+    /**

+     * The consumers' service references.

+     */

+    private ServiceReference[] m_consumersServices;

+

+    /**

+     * The consumers' services.

+     */

+    private DonutConsumer[] m_consumers;

+

+    /**

+     * The slow consumers' services.

+     */

+    private DonutConsumer[] m_quickConsumers;

+

+    /**

+     * The event tracker' instances.

+     */

+    private ComponentInstance m_eventTrackerInstance;

+

+    /**

+     * The event tracker' service references.

+     */

+    private ServiceReference m_eventTrackerService;

+

+    /**

+     * The event tracker service.

+     */

+    private EventTracker m_eventTracker;

+

+    /**

+     * The filtered consumers' instances.

+     */

+    private ComponentInstance[] m_filteredConsumersInstances;

+

+    /**

+     * The filtered consumers' service references.

+     */

+    private ServiceReference[] m_filteredConsumersServices;

+

+    /**

+     * The filtered consumers' services.

+     */

+    private DonutConsumer[] m_filteredConsumers;

+

+    /**

+     * The providers' instances with specified topics.

+     */

+    private ComponentInstance[] m_topicsProvidersInstances;

+

+    /**

+     * The providers' service references with specified topics.

+     */

+    private ServiceReference[] m_topicsProvidersServices;

+

+    /**

+     * The providers' service with specified topics.

+     */

+    private DonutProvider[] m_topicsProviders;

+

+    /**

+     * The provider that send donuts on the "foo" topic.

+     */

+    private DonutProvider m_fooProvider;

+

+    /**

+     * The provider that send donuts on the "bar" topic.

+     */

+    private DonutProvider m_barProvider;

+

+    /**

+     * The provider that send donuts on the "nut" topic.

+     */

+    private DonutProvider m_nutProvider;

+

+    /**

+     * The provider that send donuts on the "foo,bar" topics.

+     */

+    private DonutProvider m_fooBarProvider;

+

+    /**

+     * The provider that send donuts on the "bar,nut" topics.

+     */

+    private DonutProvider m_barNutProvider;

+

+    /**

+     * The provider that send donuts on the "foo,nut" topics.

+     */

+    private DonutProvider m_fooNutProvider;

+

+    /**

+     * The provider that send donuts on the "foo,bar,nut" topics.

+     */

+    private DonutProvider m_fooBarNutProvider;

+

+    /**

+     * The consumers' instances with specified topics.

+     */

+    private ComponentInstance[] m_topicsConsumersInstances;

+

+    /**

+     * The consumers' service references with specified topics.

+     */

+    private ServiceReference[] m_topicsConsumersServices;

+

+    /**

+     * The consumers' service references with specified topics.

+     */

+    private DonutConsumer[] m_topicsConsumers;

+

+    /**

+     * The consumer that receive donuts on the "foo" topic.

+     */

+    private DonutConsumer m_fooConsumer;

+

+    /**

+     * The consumer that receive donuts on the "bar" topic.

+     */

+    private DonutConsumer m_barConsumer;

+

+    /**

+     * The consumer that receive donuts on the "nut" topic.

+     */

+    private DonutConsumer m_nutConsumer;

+

+    /**

+     * The consumer that receive donuts on the "foo,bar" topics.

+     */

+    private DonutConsumer m_fooBarConsumer;

+

+    /**

+     * The consumer that receive donuts on the "bar,nut" topics.

+     */

+    private DonutConsumer m_barNutConsumer;

+

+    /**

+     * The consumer that receive donuts on the "foo,nut" topics.

+     */

+    private DonutConsumer m_fooNutConsumer;

+

+    /**

+     * The consumer that receive donuts on the "foo,bar,nut" topics.

+     */

+    private DonutConsumer m_fooBarNutConsumer;

+

+    /**

+     * Initialization before test cases.

+     * 

+     * Create all the instances

+     * 

+     * @throws UnacceptableConfiguration

+     *             something bad happened

+     * @throws MissingHandlerException

+     *             something bad happened

+     * @throws ConfigurationException

+     *             something bad happened

+     * 

+     */

+    public void setUp()

+        throws UnacceptableConfiguration, MissingHandlerException,

+        ConfigurationException {

+

+        m_utils = new EahTestUtils(context);

+        Dictionary properties = new Hashtable();

+

+        // All the providers

+        m_providersInstances = new ComponentInstance[NUMBER_OF_PROVIDERS];

+        m_providersServices = new ServiceReference[NUMBER_OF_PROVIDERS];

+        m_providers = new DonutProvider[NUMBER_OF_PROVIDERS];

+        m_synchronousProviders = new DonutProvider[NUMBER_OF_SYNCHRONOUS_PROVIDERS];

+        m_eahProviders = new DonutProvider[NUMBER_OF_EAH_PROVIDERS];

+        m_eahProvidersInstances = new ComponentInstance[NUMBER_OF_EAH_PROVIDERS];

+        m_topicsProvidersInstances = new ComponentInstance[TOPICS_LIST.length];

+        m_topicsProvidersServices = new ServiceReference[TOPICS_LIST.length];

+        m_topicsProviders = new DonutProvider[TOPICS_LIST.length];

+

+        // Create the (asynchronous) donut provider

+        properties.put("name", "asynchronous donut provider");

+        m_providersInstances[0] = m_utils.getDonutProviderFactory()

+                .createComponentInstance(properties);

+

+        // Create the synchronous donut provider

+        properties.put("name", "synchronous donut provider");

+        m_providersInstances[1] = m_utils.getSynchronousDonutProviderFactory()

+                .createComponentInstance(properties);

+

+        // Create the (asynchronous) donut event provider

+        properties.put("name", "asynchronous donut event provider");

+        m_providersInstances[2] = m_utils.getDonutEventProviderFactory()

+                .createComponentInstance(properties);

+

+        // Create the synchronous donut event provider

+        properties.put("name", "synchronous donut event provider");

+        m_providersInstances[3] = m_utils

+                .getSynchronousDonutEventProviderFactory()

+                .createComponentInstance(properties);

+

+        // Create the (asynchronous) event provider

+        properties.put("name", "asynchronous event provider");

+        m_providersInstances[4] = m_utils.getEventProviderFactory()

+                .createComponentInstance(properties);

+

+        // Create the synchronous event provider

+        properties.put("name", "synchronous event provider");

+        m_providersInstances[5] = m_utils.getSynchronousEventProviderFactory()

+                .createComponentInstance(properties);

+

+        // Get all the services references

+        for (int i = 0; i < NUMBER_OF_PROVIDERS; i++) {

+            m_providersServices[i] = IPojoTestUtils.getServiceReferenceByName(

+                    context, DonutProvider.class.getName(),

+                    m_providersInstances[i].getInstanceName());

+            m_providers[i] = (DonutProvider) context

+                    .getService(m_providersServices[i]);

+        }

+        m_synchronousProviders[0] = m_providers[1];

+        m_synchronousProviders[1] = m_providers[3];

+        m_synchronousProviders[2] = m_providers[5];

+        m_eahProviders[0] = m_providers[0];

+        m_eahProviders[1] = m_providers[1];

+        m_eahProviders[2] = m_providers[2];

+        m_eahProviders[3] = m_providers[3];

+        m_eahProvidersInstances[0] = m_providersInstances[0];

+        m_eahProvidersInstances[1] = m_providersInstances[1];

+        m_eahProvidersInstances[2] = m_providersInstances[2];

+        m_eahProvidersInstances[3] = m_providersInstances[3];

+        m_synchronousDonutEventProvider = m_providers[3];

+

+        // All the consumers

+        m_consumersInstances = new ComponentInstance[NUMBER_OF_CONSUMERS];

+        m_consumersServices = new ServiceReference[NUMBER_OF_CONSUMERS];

+        m_consumers = new DonutConsumer[NUMBER_OF_CONSUMERS];

+        m_quickConsumers = new DonutConsumer[NUMBER_OF_QUICK_CONSUMERS];

+        m_filteredConsumersInstances = new ComponentInstance[Donut.FLAVOURS.length];

+        m_filteredConsumersServices = new ServiceReference[Donut.FLAVOURS.length];

+        m_filteredConsumers = new DonutConsumer[Donut.FLAVOURS.length];

+        m_topicsConsumersInstances = new ComponentInstance[TOPICS_LIST.length];

+        m_topicsConsumersServices = new ServiceReference[TOPICS_LIST.length];

+        m_topicsConsumers = new DonutConsumer[TOPICS_LIST.length];

+

+        // Create the (quick) donut consumer

+        properties.put("name", "quick donut consumer");

+        m_consumersInstances[0] = m_utils.getDonutConsumerFactory()

+                .createComponentInstance(properties);

+

+        // Create the (quick) donut event consumer

+        properties.put("name", "quick donut event consumer");

+        m_consumersInstances[1] = m_utils.getDonutEventConsumerFactory()

+                .createComponentInstance(properties);

+

+        // Create the (quick) event consumer

+        properties.put("name", "quick event consumer");

+        m_consumersInstances[2] = m_utils.getEventConsumerFactory()

+                .createComponentInstance(properties);

+

+        properties.put("slow", Boolean.TRUE);

+

+        // Create the slow donut consumer

+        properties.put("name", "slow donut consumer");

+        m_consumersInstances[3] = m_utils.getDonutConsumerFactory()

+                .createComponentInstance(properties);

+

+        // Create the slow donut event consumer

+        properties.put("name", "slow donut event consumer");

+        m_consumersInstances[4] = m_utils.getDonutEventConsumerFactory()

+                .createComponentInstance(properties);

+

+        // Create the slow event consumer

+        properties.put("name", "slow event consumer");

+        m_consumersInstances[5] = m_utils.getEventConsumerFactory()

+                .createComponentInstance(properties);

+

+        properties.remove("slow");

+

+        // Get all the services references

+        for (int i = 0; i < NUMBER_OF_CONSUMERS; i++) {

+            m_consumersServices[i] = IPojoTestUtils.getServiceReferenceByName(

+                    context, DonutConsumer.class.getName(),

+                    m_consumersInstances[i].getInstanceName());

+            m_consumers[i] = (DonutConsumer) context

+                    .getService(m_consumersServices[i]);

+        }

+        m_quickConsumers[0] = m_consumers[0];

+        m_quickConsumers[1] = m_consumers[1];

+        m_quickConsumers[2] = m_consumers[2];

+

+        // Create the event tracker

+        properties.put("name", "event tracker");

+        m_eventTrackerInstance = m_utils.getEventTrackerFactory()

+                .createComponentInstance(properties);

+        m_eventTrackerService = IPojoTestUtils.getServiceReferenceByName(

+                context, EventTracker.class.getName(), m_eventTrackerInstance

+                        .getInstanceName());

+        m_eventTracker = (EventTracker) context

+                .getService(m_eventTrackerService);

+

+        // Create the filtered consumer

+        Dictionary filter = new Hashtable();

+        for (int i = 0; i < Donut.FLAVOURS.length; i++) {

+            String flavour = Donut.FLAVOURS[i];

+            filter.put("donut-event-subscriber", "(flavour=" + flavour + ")");

+            properties.put("name", flavour + " donut consumer");

+            properties.put("event.filter", filter);

+            m_filteredConsumersInstances[i] = m_utils

+                    .getDonutEventConsumerFactory().createComponentInstance(

+                            properties);

+            m_filteredConsumersServices[i] = IPojoTestUtils

+                    .getServiceReferenceByName(context, DonutConsumer.class

+                            .getName(), m_filteredConsumersInstances[i]

+                            .getInstanceName());

+            m_filteredConsumers[i] = (DonutConsumer) context

+                    .getService(m_filteredConsumersServices[i]);

+        }

+        properties.remove("event.filter");

+

+        // Create the providers and consumers selling and receiving donuts on

+        // specific topics

+        Dictionary topics = new Hashtable();

+        for (int i = 0; i < TOPICS_LIST.length; i++) {

+            String topicsString = TOPICS_LIST[i];

+

+            // Create provider

+            topics.put("donut-publisher", topicsString);

+            properties.put("event.topics", topics);

+            properties.put("name", topicsString + " donut provider");

+            m_topicsProvidersInstances[i] = m_utils

+                    .getSynchronousDonutProviderFactory()

+                    .createComponentInstance(properties);

+            m_topicsProvidersServices[i] = IPojoTestUtils

+                    .getServiceReferenceByName(context, DonutProvider.class

+                            .getName(), m_topicsProvidersInstances[i]

+                            .getInstanceName());

+            m_topicsProviders[i] = (DonutProvider) context

+                    .getService(m_topicsProvidersServices[i]);

+            topics.remove("donut-publisher");

+

+            // Create consumer

+            topics.put("donut-subscriber", topicsString);

+            properties.put("event.topics", topics);

+            properties.put("name", topicsString + " donut consumer");

+

+            m_topicsConsumersInstances[i] = m_utils.getDonutConsumerFactory()

+                    .createComponentInstance(properties);

+            m_topicsConsumersServices[i] = IPojoTestUtils

+                    .getServiceReferenceByName(context, DonutConsumer.class

+                            .getName(), m_topicsConsumersInstances[i]

+                            .getInstanceName());

+            m_topicsConsumers[i] = (DonutConsumer) context

+                    .getService(m_topicsConsumersServices[i]);

+            topics.remove("donut-subscriber");

+        }

+        properties.remove("event.topics");

+        m_fooProvider = m_topicsProviders[0];

+        m_barProvider = m_topicsProviders[1];

+        m_nutProvider = m_topicsProviders[2];

+        m_fooBarProvider = m_topicsProviders[3];

+        m_barNutProvider = m_topicsProviders[4];

+        m_fooNutProvider = m_topicsProviders[5];

+        m_fooBarNutProvider = m_topicsProviders[6];

+        m_fooConsumer = m_topicsConsumers[0];

+        m_barConsumer = m_topicsConsumers[1];

+        m_nutConsumer = m_topicsConsumers[2];

+        m_fooBarConsumer = m_topicsConsumers[3];

+        m_barNutConsumer = m_topicsConsumers[4];

+        m_fooNutConsumer = m_topicsConsumers[5];

+        m_fooBarNutConsumer = m_topicsConsumers[6];

+

+    }

+

+    /**

+     * Test the event handler reliability by sending events with all kinds of

+     * publisher and check they are received by all kinds of subscriber.

+     */

+    public void testReliability() {

+

+        // Flush donut list for each consumer

+        for (int i = 0; i < NUMBER_OF_CONSUMERS; i++) {

+            m_consumers[i].clearDonuts();

+        }

+

+        // Send a lot of donut with each provider

+        List sentDonuts = new ArrayList(NUMBER_OF_PROVIDERS

+                * EahTestUtils.NUMBER_OF_TESTS);

+        for (int i = 0; i < NUMBER_OF_PROVIDERS; i++) {

+            for (int j = 0; j < EahTestUtils.NUMBER_OF_TESTS; j++) {

+                sentDonuts.add(m_providers[i].sellDonut());

+            }

+        }

+

+        // Wait a respectable amount of time

+        EahTestUtils.sleep(EahTestUtils.BLACK_LIST_TIME

+                + EahTestUtils.A_LONG_TIME);

+

+        // Collect all received donuts for each consumer

+        for (int i = 0; i < NUMBER_OF_CONSUMERS; i++) {

+            List receivedDonuts = Arrays.asList(m_consumers[i].getAllDonuts());

+            assertEquals(

+                    "The number of received donuts must be the same as the number of sent donuts.",

+                    sentDonuts.size(), receivedDonuts.size());

+            assertTrue("The receiver must have eaten all sent donuts.",

+                    receivedDonuts.containsAll(sentDonuts));

+        }

+    }

+

+    /**

+     * Test the synchronism of event sending for the component.

+     * 

+     * This test consists to send synchronously a big amount of donuts and to

+     * check immediately if it has been received (by all quick consumers).

+     */

+    public void testSynchronism() {

+

+        // Flush donut list for quick consumers

+        for (int i = 0; i < NUMBER_OF_QUICK_CONSUMERS; i++) {

+            m_quickConsumers[i].clearDonuts();

+        }

+

+        // Send a lot of donuts and check they are immediately received.

+        Donut sentDonut;

+        Donut receivedDonut;

+        for (int i = 0; i < EahTestUtils.NUMBER_OF_TESTS; i++) {

+            for (int j = 0; j < NUMBER_OF_SYNCHRONOUS_PROVIDERS; j++) {

+                sentDonut = m_synchronousProviders[j].sellDonut();

+                for (int k = 0; k < NUMBER_OF_QUICK_CONSUMERS; k++) {

+                    receivedDonut = m_quickConsumers[k].getDonut();

+                    assertEquals(

+                            "The donut must have been received immediately and be the be the same as the sent one.",

+                            sentDonut, receivedDonut);

+                }

+            }

+        }

+    }

+

+    /**

+     * Test that the received events contains the instance name of the sender.

+     */

+    public void testInstanceName() {

+

+        // Flush event list of the event tracker

+        m_eventTracker.clearEvents();

+

+        // Send donuts and check the sender instance name

+        Event receivedEvent;

+        for (int i = 0; i < NUMBER_OF_EAH_PROVIDERS; i++) {

+            m_eahProviders[i].sellDonut();

+            receivedEvent = m_eventTracker.waitForEvent();

+            assertEquals(

+                    "The instance name property of the received message must be the same as the sender instance name.",

+                    m_eahProvidersInstances[i].getInstanceName(), receivedEvent

+                            .getProperty("publisher.instance.name"));

+        }

+    }

+

+    /**

+     * Test the event filtering.

+     * 

+     * This test send donuts with different flavours. Each filtered consumer

+     * must receive only a certain kind of donut. Of course, all donuts must

+     * have been received too.

+     */

+    public void testFilters() {

+

+        // The sent donuts, sorted by flavour

+        List[] sentDonuts = new List[Donut.FLAVOURS.length];

+

+        // Flush donut list for each filtered consumer

+        for (int i = 0; i < Donut.FLAVOURS.length; i++) {

+            m_filteredConsumers[i].clearDonuts();

+            sentDonuts[i] = new ArrayList(EahTestUtils.NUMBER_OF_TESTS

+                    / Donut.FLAVOURS.length);

+        }

+

+        // Send donuts

+        for (int j = 0; j < EahTestUtils.NUMBER_OF_TESTS; j++) {

+            Donut donut = m_synchronousDonutEventProvider.sellDonut();

+            sentDonuts[EahTestUtils.flavourIndex(donut.getFlavour())]

+                    .add(donut);

+        }

+

+        // Check the received donuts

+        for (int i = 0; i < Donut.FLAVOURS.length; i++) {

+            Donut[] receivedDonuts = m_filteredConsumers[i].getAllDonuts();

+            assertEquals(

+                    "The number of received donuts must be the same as the number of sent donuts with the matching flavour.",

+                    sentDonuts[i].size(), receivedDonuts.length);

+            assertTrue(

+                    "The receiver must have eaten all sent donuts matching the wanted flavour.",

+                    Arrays.asList(receivedDonuts).containsAll(sentDonuts[i]));

+        }

+

+    }

+

+    /**

+     * Test the event topic.

+     * 

+     * This test send donuts on several topics. Each consumer (who listens to

+     * one or several topics) must receive donuts sent on his specifics topics.

+     */

+    public void testTopics() {

+

+        // The sent donuts, sorted by topic

+        int foos = 0;

+        int bars = 0;

+        int nuts = 0;

+

+        // Flush consumers

+        m_fooConsumer.clearDonuts();

+        m_barConsumer.clearDonuts();

+        m_nutConsumer.clearDonuts();

+        m_fooBarConsumer.clearDonuts();

+        m_barNutConsumer.clearDonuts();

+        m_fooNutConsumer.clearDonuts();

+        m_fooBarNutConsumer.clearDonuts();

+

+        // Send donuts

+        Donut sentDonut;

+        for (int i = 0; i < EahTestUtils.NUMBER_OF_TESTS; i++) {

+            m_fooProvider.sellDonut();

+            foos++;

+

+            m_barProvider.sellDonut();

+            bars++;

+

+            m_nutProvider.sellDonut();

+            nuts++;

+

+            sentDonut = m_fooBarProvider.sellDonut();

+            foos++;

+            bars++;

+

+            sentDonut = m_barNutProvider.sellDonut();

+            bars++;

+            nuts++;

+

+            sentDonut = m_fooNutProvider.sellDonut();

+            foos++;

+            nuts++;

+

+            sentDonut = m_fooBarNutProvider.sellDonut();

+            foos++;

+            bars++;

+            nuts++;

+        }

+

+        // Check received donuts

+        assertEquals("The number of received donuts must be correct.", foos,

+                m_fooConsumer.getAllDonuts().length);

+        assertEquals("The number of received donuts must be correct.", bars,

+                m_barConsumer.getAllDonuts().length);

+        assertEquals("The number of received donuts must be correct.", nuts,

+                m_nutConsumer.getAllDonuts().length);

+        assertEquals("The number of received donuts must be correct.", foos

+                + bars, m_fooBarConsumer.getAllDonuts().length);

+        assertEquals("The number of received donuts must be correct.", bars

+                + nuts, m_barNutConsumer.getAllDonuts().length);

+        assertEquals("The number of received donuts must be correct.", foos

+                + nuts, m_fooNutConsumer.getAllDonuts().length);

+        assertEquals("The number of received donuts must be correct.", foos

+                + bars + nuts, m_fooBarNutConsumer.getAllDonuts().length);

+

+    }

+

+    /**

+     * Finalization after test cases.

+     * 

+     * Release all services references and destroy instances.

+     */

+    public void tearDown() {

+        int index;

+        for (index = 0; index < NUMBER_OF_PROVIDERS; index++) {

+            context.ungetService(m_providersServices[index]);

+            m_providersInstances[index].dispose();

+        }

+        for (index = 0; index < NUMBER_OF_CONSUMERS; index++) {

+            context.ungetService(m_consumersServices[index]);

+            m_consumersInstances[index].dispose();

+        }

+        context.ungetService(m_eventTrackerService);

+        m_eventTrackerInstance.dispose();

+        for (int i = 0; i < Donut.FLAVOURS.length; i++) {

+            context.ungetService(m_filteredConsumersServices[i]);

+            m_filteredConsumersInstances[i].dispose();

+        }

+        for (int i = 0; i < TOPICS_LIST.length; i++) {

+            context.ungetService(m_topicsProvidersServices[i]);

+            m_topicsProvidersInstances[i].dispose();

+            context.ungetService(m_topicsConsumersServices[i]);

+            m_topicsConsumersInstances[i].dispose();

+        }

+

+    }

+

+}

diff --git a/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/AsyncEventProviderImpl.java b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/AsyncEventProviderImpl.java
new file mode 100644
index 0000000..6b0da14
--- /dev/null
+++ b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/AsyncEventProviderImpl.java
@@ -0,0 +1,83 @@
+/* 

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.apache.felix.ipojo.test.donut;

+

+import java.util.Dictionary;

+import java.util.Hashtable;

+import java.util.Random;

+

+import org.apache.felix.ipojo.test.util.EahTestUtils;

+import org.osgi.service.event.Event;

+import org.osgi.service.event.EventAdmin;

+

+/**

+ * Implementation of an event vendor that directly uses the Event Admin service

+ * to post (asynchronously) raw events.

+ * 

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ * 

+ */

+public class AsyncEventProviderImpl implements DonutProvider {

+

+    /**

+     * The donut current serial number.

+     */

+    private long m_serial = 0L;

+

+    /**

+     * The name of the donut vendor.

+     */

+    private String m_name;

+

+    /**

+     * A random generator.

+     */

+    private Random m_random;

+

+    /**

+     * The Event Admin service reference.

+     */

+    private EventAdmin m_ea;

+

+    /**

+     * Construct a new donut provider. The initial serial number is randomly

+     * generated.

+     */

+    public AsyncEventProviderImpl() {

+        m_random = new Random(System.currentTimeMillis());

+    }

+

+    /**

+     * Sell a donut with a random flavour.

+     * 

+     * @return the sold donut

+     */

+    public Donut sellDonut() {

+        Dictionary rawEvent = new Hashtable();

+        Donut donut = new Donut(m_serial++, m_name, Donut.FLAVOURS[m_random

+                .nextInt(Donut.FLAVOURS.length)]);

+        rawEvent.put("food", donut);

+        m_ea.postEvent(new Event("food/donuts", rawEvent));

+        if (EahTestUtils.TRACE) {

+            System.err.println("[" + this.getClass().getSimpleName() + ":"

+                    + m_name + "] Selling donut " + donut);

+        }

+        return donut;

+    }

+}

diff --git a/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/Donut.java b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/Donut.java
new file mode 100644
index 0000000..c7b4fc5
--- /dev/null
+++ b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/Donut.java
@@ -0,0 +1,100 @@
+/* 

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.apache.felix.ipojo.test.donut;

+

+/**

+ * Donut representation.

+ * 

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class Donut {

+

+    /**

+     * All possible donut flavours.

+     */

+    public static final String[] FLAVOURS = { "unflavoured", "icing sugar",

+        "chocolate", "toffee", "strawberry", "apple" };

+

+    /**

+     * The vendor's unique donut identifier.

+     */

+    private final long m_id;

+

+    /**

+     * The name of this donut's vendor.

+     */

+    private final String m_vendorName;

+

+    /**

+     * The m_flavour of this donut.

+     */

+    private final String m_flavour;

+

+    /**

+     * Create a new donut.

+     * 

+     * @param id

+     *            the vendor's unique donut identifier

+     * @param vendorName

+     *            the name of this donut's vendor

+     * @param flavour

+     *            the m_flavour of this donut

+     */

+    public Donut(long id, String vendorName, String flavour) {

+        this.m_id = id;

+        this.m_vendorName = vendorName;

+        this.m_flavour = flavour;

+    }

+

+    /**

+     * Get the vendor's unique identifier of this donut.

+     * 

+     * @return the id

+     */

+    public long getId() {

+        return m_id;

+    }

+

+    /**

+     * Get the vendor name of this donut.

+     * 

+     * @return the name

+     */

+    public String getVendorName() {

+        return m_vendorName;

+    }

+

+    /**

+     * Get the flavour of this donut.

+     * 

+     * @return the flavour

+     */

+    public String getFlavour() {

+        return m_flavour;

+    }

+

+    /**

+     * Return the string representation of this donut.

+     * 

+     * @return this donut as a String

+     */

+    public String toString() {

+        return m_id + " " + m_flavour + " (" + m_vendorName + ")";

+    }

+}

diff --git a/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/DonutConsumer.java b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/DonutConsumer.java
new file mode 100644
index 0000000..0e5c483
--- /dev/null
+++ b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/DonutConsumer.java
@@ -0,0 +1,81 @@
+/* 

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.apache.felix.ipojo.test.donut;

+

+import org.osgi.service.event.Event;

+

+/**

+ * Specification of a donut consumer.

+ * 

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public interface DonutConsumer {

+

+    /**

+     * Donut receiver callback. This method is called when a donut is received

+     * on the listened topic.

+     * 

+     * @param donut

+     *            the received donut

+     */

+    void receiveDonut(Donut donut);

+

+    /**

+     * Event donut receiver callback. This method is called when an event is

+     * received on the listened topic.

+     * 

+     * @param event

+     *            the received event

+     */

+    void receiveEvent(Event event);

+

+    /**

+     * Clear the eaten donuts list. (Useful before tests)

+     */

+    void clearDonuts();

+

+    /**

+     * Get the first received donut and remove it from the eaten donut list.

+     * 

+     * @return the first received donut or null if no donut is available

+     */

+    Donut getDonut();

+

+    /**

+     * Get the whole list of eaten donuts.

+     * 

+     * @return the array containing all eaten donuts

+     */

+    Donut[] getAllDonuts();

+

+    /**

+     * Get the first donut if available or wait for an incoming donut. The

+     * returned donut is removed from the eaten donut list.

+     * 

+     * @return the first available donut.

+     */

+    Donut waitForDonut();

+

+    /**

+     * Return the size of the eaten donut list.

+     * 

+     * @return the size of the eaten donut list

+     */

+    int getNumberOfDonuts();

+}

diff --git a/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/DonutConsumerImpl.java b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/DonutConsumerImpl.java
new file mode 100644
index 0000000..664290f
--- /dev/null
+++ b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/DonutConsumerImpl.java
@@ -0,0 +1,205 @@
+/* 

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.apache.felix.ipojo.test.donut;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import org.apache.felix.ipojo.test.util.EahTestUtils;

+import org.osgi.service.event.Event;

+

+/**

+ * Implementation of a donut consumer.

+ * 

+ * @see Homer Simpson

+ * 

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class DonutConsumerImpl implements DonutConsumer {

+

+    /**

+     * The name of the donut consumer.

+     */

+    private String m_name;

+

+    /**

+     * The list of eaten donuts.

+     */

+    private List m_donuts = new ArrayList();

+

+    /**

+     * Is this consumer a slow eater ?

+     */

+    private boolean m_isSlow;

+

+    /**

+     * Process incoming donuts. This method is called by the receiveDonut

+     * callback.

+     * 

+     * @param donut

+     *            the received donut

+     */

+    private void doReceiveDonut(Donut donut) {

+        synchronized (m_donuts) {

+            m_donuts.add(donut);

+            m_donuts.notify();

+            if (EahTestUtils.TRACE) {

+                System.err.println("[" + this.getClass().getSimpleName() + ":"

+                        + m_name + "] Eating donut " + donut);

+            }

+        }

+    }

+

+    /**

+     * Utility method that causes the current thread to sleep.

+     * 

+     * @param millis

+     *            the number of milliseconds to wait

+     */

+    public static void sleep(long millis) {

+        long past = System.currentTimeMillis();

+        long future = past + millis;

+        long now = past;

+        while (now < future) {

+            try {

+                Thread.sleep(future - now);

+            } catch (Exception e) {

+            }

+            now = System.currentTimeMillis();

+        }

+    }

+

+    /**

+     * Donut receiver callback. This method is called when a donut is received

+     * on the listened topic.

+     * 

+     * @param donut

+     *            the received donut

+     */

+    public void receiveDonut(Donut donut) {

+        final Donut myDonut = donut;

+        if (m_isSlow) {

+            new Thread(new Runnable() {

+                public void run() {

+                    sleep(EahTestUtils.BLACK_LIST_TIME);

+                    doReceiveDonut(myDonut);

+                }

+            }, m_name + " eating " + donut).start();

+        } else {

+            doReceiveDonut(donut);

+        }

+    }

+

+    /**

+     * Event donut receiver callback. This method is called when an event is

+     * received on the listened topic.

+     * 

+     * @param event

+     *            the received event

+     */

+    public void receiveEvent(Event event) {

+        Object thing = event.getProperty("food");

+        if (Donut.class.isInstance(thing)) {

+            receiveDonut((Donut) thing);

+        } else {

+            if (EahTestUtils.TRACE) {

+                System.err.println("[" + this.getClass().getSimpleName() + ":"

+                        + m_name + "] D'oh ! Received an uneatable thing : "

+                        + thing);

+                throw new ClassCastException("I want DONUTS !");

+            }

+        }

+    }

+

+    /**

+     * Clear the eaten donuts list. (Useful before tests)

+     */

+    public void clearDonuts() {

+        synchronized (m_donuts) {

+            m_donuts.clear();

+        }

+    }

+

+    /**

+     * Get the first received donut and remove it from the eaten donut list.

+     * 

+     * @return the first received donut or null if no donut is available

+     */

+    public Donut getDonut() {

+        Donut donut = null;

+        synchronized (m_donuts) {

+            if (!m_donuts.isEmpty()) {

+                donut = (Donut) m_donuts.remove(0);

+            }

+        }

+        return donut;

+    }

+

+    /**

+     * Get the whole list of eaten donuts.

+     * 

+     * @return the array containing all eaten donuts

+     */

+    public Donut[] getAllDonuts() {

+        Donut[] donuts = new Donut[0];

+        synchronized (m_donuts) {

+            donuts = (Donut[]) m_donuts.toArray(donuts);

+            m_donuts.clear();

+        }

+        return donuts;

+    }

+

+    /**

+     * Get the first donut if available or wait for an incoming donut. The

+     * returned donut is removed from the eaten donut list.

+     * 

+     * @return the first available donut.

+     */

+    public Donut waitForDonut() {

+        Donut donut = null;

+        synchronized (m_donuts) {

+            while (donut == null) {

+                if (m_donuts.isEmpty()) {

+                    try {

+                        m_donuts.wait();

+                    } catch (InterruptedException e) {

+                        // Thanks Checkstyle to forbid empty catch statements

+                        // ;-(

+                    }

+                } else {

+                    donut = (Donut) m_donuts.remove(0);

+                }

+            }

+        }

+        return donut;

+    }

+

+    /**

+     * Return the size of the eaten donut list.

+     * 

+     * @return the size of the eaten donut list

+     */

+    public int getNumberOfDonuts() {

+        int length;

+        synchronized (m_donuts) {

+            length = m_donuts.size();

+        }

+        return length;

+    }

+}

diff --git a/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/DonutEventProviderImpl.java b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/DonutEventProviderImpl.java
new file mode 100644
index 0000000..154eff4
--- /dev/null
+++ b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/DonutEventProviderImpl.java
@@ -0,0 +1,83 @@
+/* 

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.apache.felix.ipojo.test.donut;

+

+import java.util.Dictionary;

+import java.util.Hashtable;

+import java.util.Random;

+

+import org.apache.felix.ipojo.handlers.event.publisher.Publisher;

+import org.apache.felix.ipojo.test.util.EahTestUtils;

+

+/**

+ * Implementation of a donut vendor that send raw events instead of sending

+ * Donut objects.

+ * 

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ * 

+ */

+public class DonutEventProviderImpl implements DonutProvider {

+

+    /**

+     * The donut current serial number.

+     */

+    private long m_serial = 0L;

+

+    /**

+     * The name of the donut vendor.

+     */

+    private String m_name;

+

+    /**

+     * A random generator.

+     */

+    private Random m_random;

+

+    /**

+     * The event publisher of the donut vendor.

+     */

+    private Publisher m_publisher;

+

+    /**

+     * Construct a new donut provider. The initial serial number is randomly

+     * generated.

+     */

+    public DonutEventProviderImpl() {

+        m_random = new Random(System.currentTimeMillis());

+    }

+

+    /**

+     * Sell a donut with a random flavour.

+     * 

+     * @return the sold donut

+     */

+    public Donut sellDonut() {

+        Dictionary rawEvent = new Hashtable();

+        String flavour = Donut.FLAVOURS[m_random.nextInt(Donut.FLAVOURS.length)];

+        Donut donut = new Donut(m_serial++, m_name, flavour);

+        rawEvent.put("food", donut);

+        rawEvent.put("flavour", flavour);

+        m_publisher.send(rawEvent);

+        if (EahTestUtils.TRACE) {

+            System.err.println("[" + this.getClass().getSimpleName() + ":"

+                    + m_name + "] Selling event donut " + donut);

+        }

+        return donut;

+    }

+}

diff --git a/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/DonutProvider.java b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/DonutProvider.java
new file mode 100644
index 0000000..17a1b92
--- /dev/null
+++ b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/DonutProvider.java
@@ -0,0 +1,35 @@
+/* 

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.apache.felix.ipojo.test.donut;

+

+/**

+ * Specification of a donut vendor.

+ * 

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ * 

+ */

+public interface DonutProvider {

+

+    /**

+     * Sell a donut with a random flavour.

+     * 

+     * @return the sold donut

+     */

+    Donut sellDonut();

+}

diff --git a/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/DonutProviderImpl.java b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/DonutProviderImpl.java
new file mode 100644
index 0000000..961eb1e
--- /dev/null
+++ b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/DonutProviderImpl.java
@@ -0,0 +1,79 @@
+/* 

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.apache.felix.ipojo.test.donut;

+

+import java.util.Random;

+

+import org.apache.felix.ipojo.handlers.event.publisher.Publisher;

+import org.apache.felix.ipojo.test.util.EahTestUtils;

+

+/**

+ * The standard implementation of a donut vendor.

+ * 

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ * 

+ */

+public class DonutProviderImpl implements DonutProvider {

+

+    

+

+    /**

+     * The donut current serial number.

+     */

+    private long m_serial = 0L;

+

+    /**

+     * The name of the donut vendor.

+     */

+    private String m_name;

+

+    /**

+     * A random generator.

+     */

+    private Random m_random;

+

+    /**

+     * The donut publisher of the vendor.

+     */

+    private Publisher m_publisher;

+

+    /**

+     * Construct a new donut provider. The initial serial number is randomly

+     * generated.

+     */

+    public DonutProviderImpl() {

+        m_random = new Random(System.currentTimeMillis());

+    }

+

+    /**

+     * Sell a donut with a random flavour.

+     * 

+     * @return the sold donut

+     */

+    public Donut sellDonut() {

+        Donut donut = new Donut(m_serial++, m_name, Donut.FLAVOURS[m_random

+                .nextInt(Donut.FLAVOURS.length)]);

+        m_publisher.sendData(donut);

+        if (EahTestUtils.TRACE) {

+            System.err.println("[" + this.getClass().getSimpleName() + ":"

+                    + m_name + "] Selling donut " + donut);

+        }

+        return donut;

+    }

+}

diff --git a/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/EventConsumerImpl.java b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/EventConsumerImpl.java
new file mode 100644
index 0000000..78b6f9f
--- /dev/null
+++ b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/EventConsumerImpl.java
@@ -0,0 +1,217 @@
+/* 

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.apache.felix.ipojo.test.donut;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import org.apache.felix.ipojo.test.util.EahTestUtils;

+import org.osgi.service.event.Event;

+import org.osgi.service.event.EventHandler;

+

+/**

+ * Implementation of a donut consumer.

+ * 

+ * @see Homer Simpson

+ * 

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class EventConsumerImpl implements DonutConsumer, EventHandler {

+

+    /**

+     * The name of the donut consumer.

+     */

+    private String m_name;

+

+    /**

+     * The list of eaten donuts.

+     */

+    private List m_donuts = new ArrayList();

+

+    /**

+     * Is this consumer a slow eater ?

+     */

+    private boolean m_isSlow;

+

+    /**

+     * Process incoming donuts. This method is called by the receiveDonut

+     * callback.

+     * 

+     * @param donut

+     *            the received donut

+     */

+    private void doReceiveDonut(Donut donut) {

+        synchronized (m_donuts) {

+            m_donuts.add(donut);

+            m_donuts.notify();

+            if (EahTestUtils.TRACE) {

+                System.err.println("[" + this.getClass().getSimpleName() + ":"

+                        + m_name + "] Eating donut " + donut);

+            }

+        }

+    }

+

+    /**

+     * Utility method that causes the current thread to sleep.

+     * 

+     * @param millis

+     *            the number of milliseconds to wait

+     */

+    public static void sleep(long millis) {

+        long past = System.currentTimeMillis();

+        long future = past + millis;

+        long now = past;

+        while (now < future) {

+            try {

+                Thread.sleep(future - now);

+            } catch (Exception e) {

+            }

+            now = System.currentTimeMillis();

+        }

+    }

+

+    /**

+     * Donut receiver callback. This method is called when a donut is received

+     * on the listened topic.

+     * 

+     * @param donut

+     *            the received donut

+     */

+    public void receiveDonut(Donut donut) {

+        final Donut myDonut = donut;

+        if (m_isSlow) {

+            new Thread(new Runnable() {

+                public void run() {

+                    sleep(EahTestUtils.BLACK_LIST_TIME);

+                    doReceiveDonut(myDonut);

+                }

+            }, m_name + " eating " + donut).start();

+        } else {

+            doReceiveDonut(donut);

+        }

+    }

+

+    /**

+     * Event donut receiver callback. This method is called when an event is

+     * received on the listened topic.

+     * 

+     * @param event

+     *            the received event

+     */

+    public void receiveEvent(Event event) {

+        Object thing = event.getProperty("food");

+        if (Donut.class.isInstance(thing)) {

+            receiveDonut((Donut) thing);

+        } else {

+            if (EahTestUtils.TRACE) {

+                System.err.println("[" + this.getClass().getSimpleName() + ":"

+                        + m_name + "] D'oh ! Received an uneatable thing : "

+                        + thing);

+                throw new ClassCastException("I want DONUTS !");

+            }

+        }

+    }

+

+    /**

+     * Event receiver callback. This method is called by the event admin service

+     * when a event is received.

+     * 

+     * @param event

+     *            the received event

+     */

+    public void handleEvent(Event event) {

+        receiveEvent(event);

+    }

+

+    /**

+     * Clear the eaten donuts list. (Useful before tests)

+     */

+    public void clearDonuts() {

+        synchronized (m_donuts) {

+            m_donuts.clear();

+        }

+    }

+

+    /**

+     * Get the first received donut and remove it from the eaten donut list.

+     * 

+     * @return the first received donut or null if no donut is available

+     */

+    public Donut getDonut() {

+        Donut donut = null;

+        synchronized (m_donuts) {

+            if (!m_donuts.isEmpty()) {

+                donut = (Donut) m_donuts.remove(0);

+            }

+        }

+        return donut;

+    }

+

+    /**

+     * Get the whole list of eaten donuts.

+     * 

+     * @return the array containing all eaten donuts

+     */

+    public Donut[] getAllDonuts() {

+        Donut[] donuts = new Donut[0];

+        synchronized (m_donuts) {

+            donuts = (Donut[]) m_donuts.toArray(donuts);

+            m_donuts.clear();

+        }

+        return donuts;

+    }

+

+    /**

+     * Get the first donut if available or wait for an incoming donut. The

+     * returned donut is removed from the eaten donut list.

+     * 

+     * @return the first available donut.

+     */

+    public Donut waitForDonut() {

+        Donut donut = null;

+        synchronized (m_donuts) {

+            while (donut == null) {

+                if (m_donuts.isEmpty()) {

+                    try {

+                        m_donuts.wait();

+                    } catch (InterruptedException e) {

+                        // Thanks Checkstyle to forbid empty catch statements

+                        // ;-(

+                    }

+                } else {

+                    donut = (Donut) m_donuts.remove(0);

+                }

+            }

+        }

+        return donut;

+    }

+

+    /**

+     * Return the size of the eaten donut list.

+     * 

+     * @return the size of the eaten donut list

+     */

+    public int getNumberOfDonuts() {

+        int length;

+        synchronized (m_donuts) {

+            length = m_donuts.size();

+        }

+        return length;

+    }

+}

diff --git a/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/EventTracker.java b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/EventTracker.java
new file mode 100644
index 0000000..eaca6c5
--- /dev/null
+++ b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/EventTracker.java
@@ -0,0 +1,63 @@
+/* 

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.apache.felix.ipojo.test.donut;

+

+import org.osgi.service.event.Event;

+

+/**

+ * Specification of an event tracker.

+ * 

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public interface EventTracker {

+

+    /**

+     * Clear the received events list. (Useful before tests)

+     */

+    void clearEvents();

+

+    /**

+     * Get the first received event and remove it from the events list.

+     * 

+     * @return the first received event or null if no event is available

+     */

+    Event getEvent();

+

+    /**

+     * Get the whole list of received events.

+     * 

+     * @return the array containing all received events

+     */

+    Event[] getAllEvents();

+

+    /**

+     * Get the first event if available or wait for an incoming event. The

+     * returned event is removed from the eaten event list.

+     * 

+     * @return the first available event.

+     */

+    Event waitForEvent();

+

+    /**

+     * Return the size of the events list.

+     * 

+     * @return the size of the events list

+     */

+    int getNumberOfEvents();

+}

diff --git a/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/EventTrackerImpl.java b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/EventTrackerImpl.java
new file mode 100644
index 0000000..cb46662
--- /dev/null
+++ b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/EventTrackerImpl.java
@@ -0,0 +1,159 @@
+/* 

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.apache.felix.ipojo.test.donut;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import org.apache.felix.ipojo.test.util.EahTestUtils;

+import org.osgi.service.event.Event;

+import org.osgi.service.event.EventHandler;

+

+/**

+ * Implementation of an event tracker.

+ * 

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class EventTrackerImpl implements EventTracker, EventHandler {

+

+    /**

+     * The name of the event tracker.

+     */

+    private String m_name;

+

+    /**

+     * The list of received events

+     */

+    private List m_events = new ArrayList();

+

+    /**

+     * Event receiver callback. This method is called by the event admin service

+     * when a event is received.

+     * 

+     * @param event

+     *            the received event

+     */

+    public void handleEvent(Event event) {

+        synchronized (m_events) {

+            m_events.add(event);

+            m_events.notify();

+            if (EahTestUtils.TRACE) {

+                System.err.println("[" + this.getClass().getSimpleName() + ":"

+                        + m_name + "] Event received : " + event);

+            }

+        }

+    }

+

+    /**

+     * Clear the received events list. (Useful before tests)

+     */

+    public void clearEvents() {

+        synchronized (m_events) {

+            m_events.clear();

+        }

+    }

+

+    /**

+     * Get the first received event and remove it from the events list.

+     * 

+     * @return the first received event or null if no event is available

+     */

+    public Event getEvent() {

+        Event event = null;

+        synchronized (m_events) {

+            if (!m_events.isEmpty()) {

+                event = (Event) m_events.remove(0);

+            }

+        }

+        return event;

+    }

+

+    /**

+     * Get the whole list of received events.

+     * 

+     * @return the array containing all received events

+     */

+    public Event[] getAllEvents() {

+        Event[] events = new Event[0];

+        synchronized (m_events) {

+            events = (Event[]) m_events.toArray(events);

+            m_events.clear();

+        }

+        return events;

+    }

+

+    /**

+     * Get the first event if available or wait for an incoming event. The

+     * returned event is removed from the eaten event list.

+     * 

+     * @return the first available event.

+     */

+    public Event waitForEvent() {

+        Event event = null;

+        synchronized (m_events) {

+            while (event == null) {

+                if (m_events.isEmpty()) {

+                    try {

+                        m_events.wait();

+                    } catch (InterruptedException e) {

+                        // Thanks Checkstyle to forbid empty catch statements

+                        // ;-(

+                    }

+                } else {

+                    event = (Event) m_events.remove(0);

+                }

+            }

+        }

+        return event;

+    }

+

+    /**

+     * Return the size of the events list.

+     * 

+     * @return the size of the events list

+     */

+    public int getNumberOfEvents() {

+        int length;

+        synchronized (m_events) {

+            length = m_events.size();

+        }

+        return length;

+    }

+

+    /**

+     * Return the string representation of a given event.

+     * 

+     * @return the string representation of a given event

+     */

+    public static String eventToString(Event e) {

+        StringBuilder buf = new StringBuilder();

+        buf.append("[" + e.getTopic() + "] {");

+

+        String[] properties = e.getPropertyNames();

+        int n = properties.length - 1;

+        for (int i = 0; i <= n; i++) {

+            String name = properties[i];

+            buf.append(name + "=" + e.getProperty(name));

+            if (i != n)

+                buf.append(", ");

+        }

+        buf.append("}");

+        return buf.toString();

+    }

+}

diff --git a/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/SyncEventProviderImpl.java b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/SyncEventProviderImpl.java
new file mode 100644
index 0000000..b8bfc8b
--- /dev/null
+++ b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/donut/SyncEventProviderImpl.java
@@ -0,0 +1,83 @@
+/* 

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.apache.felix.ipojo.test.donut;

+

+import java.util.Dictionary;

+import java.util.Hashtable;

+import java.util.Random;

+

+import org.apache.felix.ipojo.test.util.EahTestUtils;

+import org.osgi.service.event.Event;

+import org.osgi.service.event.EventAdmin;

+

+/**

+ * Implementation of an event vendor that directly uses the Event Admin service

+ * to send (synchronously) raw events.

+ * 

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ * 

+ */

+public class SyncEventProviderImpl implements DonutProvider {

+

+    /**

+     * The donut current serial number.

+     */

+    private long m_serial = 0L;

+

+    /**

+     * The name of the donut vendor.

+     */

+    private String m_name;

+

+    /**

+     * A random generator.

+     */

+    private Random m_random;

+

+    /**

+     * The Event Admin service reference.

+     */

+    private EventAdmin m_ea;

+

+    /**

+     * Construct a new donut provider. The initial serial number is randomly

+     * generated.

+     */

+    public SyncEventProviderImpl() {

+        m_random = new Random(System.currentTimeMillis());

+    }

+

+    /**

+     * Sell a donut with a random flavour.

+     * 

+     * @return the sold donut

+     */

+    public Donut sellDonut() {

+        Dictionary rawEvent = new Hashtable();

+        Donut donut = new Donut(m_serial++, m_name, Donut.FLAVOURS[m_random

+                .nextInt(Donut.FLAVOURS.length)]);

+        rawEvent.put("food", donut);

+        m_ea.sendEvent(new Event("food/donuts", rawEvent));

+        if (EahTestUtils.TRACE) {

+            System.err.println("[" + this.getClass().getSimpleName() + ":"

+                    + m_name + "] Selling donut " + donut);

+        }

+        return donut;

+    }

+}

diff --git a/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/util/EahTestUtils.java b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/util/EahTestUtils.java
new file mode 100644
index 0000000..5345d01
--- /dev/null
+++ b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/util/EahTestUtils.java
@@ -0,0 +1,197 @@
+/* 

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.apache.felix.ipojo.test.util;

+

+import org.apache.felix.ipojo.Factory;

+import org.apache.felix.ipojo.test.donut.Donut;

+import org.osgi.framework.BundleContext;

+

+/**

+ * Useful variables used for the tests of the Event Admin Handler.

+ * 

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class EahTestUtils {

+

+    /**

+     * Enable debug messages ?

+     */

+    public static final boolean TRACE = false;

+

+    /**

+     * The number of tests to execute.

+     */

+    public static final int NUMBER_OF_TESTS = 50;

+

+    /**

+     * The time that is normally necessary to cause a blacklist from the event

+     * admin service.

+     */

+    public static final long BLACK_LIST_TIME = 5000L;

+    

+    /**

+     * The long amount of time.

+     */

+    public static final long A_LONG_TIME = 1000L;

+

+    /**

+     * The bundle context.

+     */

+    private BundleContext m_context;

+

+    /**

+     * Construct a new Event Admin Handler Tests utility instance.

+     * 

+     * @param context

+     *            the bundle context

+     */

+    public EahTestUtils(BundleContext context) {

+        m_context = context;

+    }

+

+    /**

+     * Return the (asynchronous) donut provider factory.

+     * 

+     * @return the (asynchronous) donut provider factory

+     */

+    public Factory getDonutProviderFactory() {

+        return IPojoTestUtils.getFactoryByName(m_context, "donut-provider");

+    }

+

+    /**

+     * Return the synchronous donut provider factory.

+     * 

+     * @return the synchronous donut provider factory

+     */

+    public Factory getSynchronousDonutProviderFactory() {

+        return IPojoTestUtils.getFactoryByName(m_context,

+                "synchronous-donut-provider");

+    }

+

+    /**

+     * Return the (asynchronous) donut event provider factory.

+     * 

+     * @return the (asynchronous) donut event provider factory

+     */

+    public Factory getDonutEventProviderFactory() {

+        return IPojoTestUtils.getFactoryByName(m_context,

+                "donut-event-provider");

+    }

+

+    /**

+     * Return the synchronous donut event provider factory.

+     * 

+     * @return the synchronous donut event provider factory

+     */

+    public Factory getSynchronousDonutEventProviderFactory() {

+        return IPojoTestUtils.getFactoryByName(m_context,

+                "synchronous-donut-event-provider");

+    }

+

+    /**

+     * Return the event provider factory.

+     * 

+     * @return the event provider factory

+     */

+    public Factory getEventProviderFactory() {

+        return IPojoTestUtils.getFactoryByName(m_context, "event-provider");

+    }

+

+    /**

+     * Return the synchronous event provider factory.

+     * 

+     * @return the synchronous event provider factory

+     */

+    public Factory getSynchronousEventProviderFactory() {

+        return IPojoTestUtils.getFactoryByName(m_context,

+                "synchronous-event-provider");

+    }

+

+    /**

+     * Return the donut consumer factory.

+     * 

+     * @return the donut consumer factory

+     */

+    public Factory getDonutConsumerFactory() {

+        return IPojoTestUtils.getFactoryByName(m_context, "donut-consumer");

+    }

+

+    /**

+     * Return the donut event consumer factory.

+     * 

+     * @return the donut event consumer factory

+     */

+    public Factory getDonutEventConsumerFactory() {

+        return IPojoTestUtils.getFactoryByName(m_context,

+                "donut-event-consumer");

+    }

+

+    /**

+     * Return the event consumer factory.

+     * 

+     * @return the event consumer factory

+     */

+    public Factory getEventConsumerFactory() {

+        return IPojoTestUtils.getFactoryByName(m_context, "event-consumer");

+    }

+

+    /**

+     * Return the event tracker.

+     * 

+     * @return the event consumer factory

+     */

+    public Factory getEventTrackerFactory() {

+        return IPojoTestUtils.getFactoryByName(m_context, "event-tracker");

+    }

+

+    /**

+     * Utility method that causes the current thread to sleep.

+     * 

+     * @param millis

+     *            the number of milliseconds to wait

+     */

+    public static void sleep(long millis) {

+        long past = System.currentTimeMillis();

+        long future = past + millis;

+        long now = past;

+        if (TRACE) {

+            System.err.println("Sleeping for " + millis + "ms");

+        }

+        while (now < future) {

+            try {

+                Thread.sleep(future - now);

+            } catch (Exception e) {

+            }

+            now = System.currentTimeMillis();

+        }

+    }

+

+    /**

+     * Return the index of the given donut flavour in the flavour array.

+     * 

+     * @return the index of the given flavour or -1 if not found

+     */

+    public static int flavourIndex(String flavour) {

+        for (int i = 0; i < Donut.FLAVOURS.length; i++) {

+            if (Donut.FLAVOURS[i].equals(flavour))

+                return i;

+        }

+        return -1;

+    }

+}

diff --git a/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/util/IPojoTestUtils.java b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/util/IPojoTestUtils.java
new file mode 100644
index 0000000..14a4a96
--- /dev/null
+++ b/ipojo/tests/handler/eventadmin/src/main/java/org/apache/felix/ipojo/test/util/IPojoTestUtils.java
@@ -0,0 +1,307 @@
+/* 

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.apache.felix.ipojo.test.util;

+

+import java.util.Dictionary;

+import java.util.Properties;

+

+import org.apache.felix.ipojo.ComponentInstance;

+import org.apache.felix.ipojo.Factory;

+import org.apache.felix.ipojo.Handler;

+import org.apache.felix.ipojo.HandlerFactory;

+import org.apache.felix.ipojo.ServiceContext;

+import org.osgi.framework.BundleContext;

+import org.osgi.framework.InvalidSyntaxException;

+import org.osgi.framework.ServiceReference;

+import org.osgi.service.cm.ManagedServiceFactory;

+

+/**

+ * Useful iPOJO methods used for the tests of the Event Admin Handler.

+ * 

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class IPojoTestUtils {

+

+    public static ComponentInstance getComponentInstance(BundleContext bc,

+            String factoryName, Dictionary configuration) {

+        Factory fact = getFactoryByName(bc, factoryName);

+

+        if (fact == null) {

+            System.err.println("Factory " + factoryName + " not found");

+            return null;

+        }

+

+        try {

+            return fact.createComponentInstance(configuration);

+        } catch (Exception e) {

+            System.err.println("Cannot create the instance from " + factoryName

+                    + " : " + e.getMessage());

+            e.printStackTrace();

+            return null;

+        }

+    }

+

+    public static ComponentInstance getComponentInstance(ServiceContext bc,

+            String factoryName, Dictionary configuration) {

+        Factory fact = getFactoryByName(bc, factoryName);

+

+        if (fact == null) {

+            return null;

+        }

+

+        if (fact.isAcceptable(configuration)) {

+            try {

+                return fact.createComponentInstance(configuration);

+            } catch (Exception e) {

+                System.err.println(e.getMessage());

+                e.printStackTrace();

+                return null;

+            }

+        } else {

+            System.err

+                    .println("Configuration not accepted by : " + factoryName);

+            return null;

+        }

+    }

+

+    public static ComponentInstance getComponentInstanceByName(

+            BundleContext bc, String factoryName, String name) {

+        Factory fact = getFactoryByName(bc, factoryName);

+

+        if (fact == null) {

+            System.err.println("Factory " + factoryName + " not found");

+            return null;

+        }

+

+        try {

+            Properties props = new Properties();

+            props.put("name", name);

+            return fact.createComponentInstance(props);

+        } catch (Exception e) {

+            System.err.println("Cannot create the instance from " + factoryName

+                    + " : " + e.getMessage());

+            e.printStackTrace();

+            return null;

+        }

+    }

+

+    public static Factory getFactoryByName(BundleContext bc, String factoryName) {

+        ServiceReference[] refs;

+        try {

+            refs = bc.getServiceReferences(Factory.class.getName(),

+                    "(factory.name=" + factoryName + ")");

+            if (refs == null) {

+                System.err.println("Cannot get the factory " + factoryName);

+                return null;

+            }

+            return (Factory) bc.getService(refs[0]);

+        } catch (InvalidSyntaxException e) {

+            System.err.println("Cannot get the factory " + factoryName + " : "

+                    + e.getMessage());

+            return null;

+        }

+    }

+

+    public static Factory getFactoryByName(ServiceContext bc, String factoryName) {

+        ServiceReference[] refs;

+        try {

+            refs = bc.getServiceReferences(Factory.class.getName(),

+                    "(factory.name=" + factoryName + ")");

+            if (refs == null) {

+                return null;

+            }

+            return (Factory) bc.getService(refs[0]);

+        } catch (InvalidSyntaxException e) {

+            System.err.println("Cannot get the factory " + factoryName + " : "

+                    + e.getMessage());

+            return null;

+        }

+    }

+

+    public static HandlerFactory getHandlerFactoryByName(BundleContext bc,

+            String factoryName) {

+        ServiceReference[] refs;

+        try {

+            refs = bc.getServiceReferences(Factory.class.getName(), "("

+                    + Handler.HANDLER_NAME_PROPERTY + "=" + factoryName + ")");

+            if (refs == null) {

+                System.err.println("Cannot get the factory " + factoryName);

+                return null;

+            }

+            return (HandlerFactory) bc.getService(refs[0]);

+        } catch (InvalidSyntaxException e) {

+            System.err.println("Cannot get the factory " + factoryName + " : "

+                    + e.getMessage());

+            return null;

+        }

+    }

+

+    public static Object getServiceObject(BundleContext bc, String itf,

+            String filter) {

+        ServiceReference ref = getServiceReference(bc, itf, filter);

+        if (ref != null) {

+            return bc.getService(ref);

+        } else {

+            return null;

+        }

+    }

+

+    public static Object getServiceObject(ServiceContext bc, String itf,

+            String filter) {

+        ServiceReference ref = getServiceReference(bc, itf, filter);

+        if (ref != null) {

+            return bc.getService(ref);

+        } else {

+            return null;

+        }

+    }

+

+    public static Object[] getServiceObjects(BundleContext bc, String itf,

+            String filter) {

+        ServiceReference[] refs = getServiceReferences(bc, itf, filter);

+        if (refs != null) {

+            Object[] list = new Object[refs.length];

+            for (int i = 0; i < refs.length; i++) {

+                list[i] = bc.getService(refs[i]);

+            }

+            return list;

+        } else {

+            return new Object[0];

+        }

+    }

+

+    public static Object[] getServiceObjects(ServiceContext bc, String itf,

+            String filter) {

+        ServiceReference[] refs = getServiceReferences(bc, itf, filter);

+        if (refs != null) {

+            Object[] list = new Object[refs.length];

+            for (int i = 0; i < refs.length; i++) {

+                list[i] = bc.getService(refs[i]);

+            }

+            return list;

+        } else {

+            return new Object[0];

+        }

+    }

+

+    public static ServiceReference getServiceReference(BundleContext bc,

+            String itf, String filter) {

+        ServiceReference[] refs = null;

+        try {

+            refs = bc.getServiceReferences(itf, filter);

+        } catch (InvalidSyntaxException e) {

+            System.err.println("Invalid Filter : " + filter);

+        }

+        if (refs == null) {

+            return null;

+        } else {

+            return refs[0];

+        }

+    }

+

+    public static ServiceReference getServiceReference(ServiceContext bc,

+            String itf, String filter) {

+        ServiceReference[] refs = null;

+        try {

+            refs = bc.getServiceReferences(itf, filter);

+        } catch (InvalidSyntaxException e) {

+            System.err.println("Invalid Filter : " + filter);

+        }

+        if (refs == null) {

+            return null;

+        } else {

+            return refs[0];

+        }

+    }

+

+    public static ServiceReference getServiceReferenceByName(BundleContext bc,

+            String itf, String name) {

+        ServiceReference[] refs = null;

+        String filter = null;

+        if (itf.equals(Factory.class.getName())

+                || itf.equals(ManagedServiceFactory.class.getName())) {

+            filter = "(" + "factory.name" + "=" + name + ")";

+        } else {

+            filter = "(" + "instance.name" + "=" + name + ")";

+        }

+        try {

+            refs = bc.getServiceReferences(itf, filter);

+        } catch (InvalidSyntaxException e) {

+            System.err.println("Invalid Filter : " + filter);

+        }

+        if (refs == null) {

+            return null;

+        } else {

+            return refs[0];

+        }

+    }

+

+    public static ServiceReference getServiceReferenceByName(ServiceContext bc,

+            String itf, String name) {

+        ServiceReference[] refs = null;

+        String filter = null;

+        if (itf.equals(Factory.class.getName())

+                || itf.equals(ManagedServiceFactory.class.getName())) {

+            filter = "(" + "factory.name" + "=" + name + ")";

+        } else {

+            filter = "(" + "instance.name" + "=" + name + ")";

+        }

+        try {

+            refs = bc.getServiceReferences(itf, filter);

+        } catch (InvalidSyntaxException e) {

+            System.err.println("Invalid Filter : " + filter);

+        }

+        if (refs == null) {

+            return null;

+        } else {

+            return refs[0];

+        }

+    }

+

+    public static ServiceReference[] getServiceReferences(BundleContext bc,

+            String itf, String filter) {

+        ServiceReference[] refs = null;

+        try {

+            refs = bc.getServiceReferences(itf, filter);

+        } catch (InvalidSyntaxException e) {

+            System.err.println("Invalid Filter : " + filter);

+        }

+        if (refs == null) {

+            return new ServiceReference[0];

+        } else {

+            return refs;

+        }

+    }

+

+    public static ServiceReference[] getServiceReferences(ServiceContext bc,

+            String itf, String filter) {

+        ServiceReference[] refs = null;

+        try {

+            refs = bc.getServiceReferences(itf, filter);

+        } catch (InvalidSyntaxException e) {

+            System.err.println("Invalid Filter : " + filter);

+        }

+        if (refs == null) {

+            return new ServiceReference[0];

+        } else {

+            return refs;

+        }

+    }

+

+}

diff --git a/ipojo/tests/handler/eventadmin/src/main/resources/metadata.xml b/ipojo/tests/handler/eventadmin/src/main/resources/metadata.xml
new file mode 100644
index 0000000..977cb3f
--- /dev/null
+++ b/ipojo/tests/handler/eventadmin/src/main/resources/metadata.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<ipojo xmlns:ev="org.apache.felix.ipojo.handlers.event.EventAdminHandler">

+	

+	<!-- The (asynchronous) donut provider -->

+	<component className="org.apache.felix.ipojo.test.donut.DonutProviderImpl"

+		name="donut-provider">

+		<!-- Expose the donut provider service -->

+		<provides interface="org.apache.felix.ipojo.test.donut.DonutProvider">

+			<property name="name" field="m_name" value="Unknown donut vendor"/>

+		</provides>

+		<!-- Donut publisher -->

+		<ev:publisher name="donut-publisher" field="m_publisher"

+			topics="food/donuts" data-key="food" synchronous="false"/>

+	</component>

+	

+	<!-- The synchronous donut provider -->

+	<component className="org.apache.felix.ipojo.test.donut.DonutProviderImpl"

+		name="synchronous-donut-provider">

+		<!-- Expose the donut provider service -->

+		<provides interface="org.apache.felix.ipojo.test.donut.DonutProvider">

+			<property name="name" field="m_name" value="Unknown donut vendor"/>

+		</provides>

+		<!-- Donut publisher -->

+		<ev:publisher name="donut-publisher" field="m_publisher"

+			topics="food/donuts" data-key="food" synchronous="true"/>

+	</component>

+	

+	<!-- The (asynchronous) donut event provider -->

+	<component

+		className="org.apache.felix.ipojo.test.donut.DonutEventProviderImpl"

+		name="donut-event-provider">

+		<!-- Expose the donut provider service -->

+		<provides interface="org.apache.felix.ipojo.test.donut.DonutProvider">

+			<property name="name" field="m_name" value="Unknown donut vendor"/>

+		</provides>

+		<!-- Raw events publisher -->

+		<ev:publisher name="event-publisher" field="m_publisher"

+			topics="food/donuts" synchronous="false"/>

+	</component>

+	

+	<!-- The synchronous donut event provider -->

+	<component

+		className="org.apache.felix.ipojo.test.donut.DonutEventProviderImpl"

+		name="synchronous-donut-event-provider">

+		<!-- Expose the donut provider service -->

+		<provides interface="org.apache.felix.ipojo.test.donut.DonutProvider">

+			<property name="name" field="m_name" value="Unknown donut vendor"/>

+		</provides>

+		<!-- Raw events publisher -->

+		<ev:publisher name="event-publisher" field="m_publisher"

+			topics="food/donuts" synchronous="true"/>

+	</component>

+	

+	<!-- The (asynchronous) event provider -->

+	<component

+		className="org.apache.felix.ipojo.test.donut.AsyncEventProviderImpl"

+		name="event-provider">

+		<!-- Expose the donut provider service -->

+		<provides interface="org.apache.felix.ipojo.test.donut.DonutProvider">

+			<property name="name" field="m_name" value="Unknown donut vendor"/>

+		</provides>

+		<!-- Direcly interacts with the Event Admin service -->

+		<requires field="m_ea"/>

+	</component>

+	

+	<!-- The synchronous event provider -->

+	<component

+		className="org.apache.felix.ipojo.test.donut.SyncEventProviderImpl"

+		name="synchronous-event-provider">

+		<!-- Expose the donut provider service -->

+		<provides interface="org.apache.felix.ipojo.test.donut.DonutProvider">

+			<property name="name" field="m_name" value="Unknown donut vendor"/>

+		</provides>

+		<!-- Direcly interacts with the Event Admin service -->

+		<requires field="m_ea"/>

+	</component>

+	

+	<!-- The donut consumer -->

+	<component className="org.apache.felix.ipojo.test.donut.DonutConsumerImpl"

+		name="donut-consumer">

+		<!-- Expose the donut consumer service -->

+		<provides interface="org.apache.felix.ipojo.test.donut.DonutConsumer">

+			<property name="name" field="m_name" value="Unknown donut consumer"/>

+			<property name="slow" field="m_isSlow" value="false"/>

+		</provides>

+		<!-- Donut events subscriber -->

+		<ev:subscriber name="donut-subscriber" callback="receiveDonut"

+			topics="food/donuts" data-key="food"

+			data-type="org.apache.felix.ipojo.test.donut.Donut"/>

+	</component>

+	

+	<!-- The donut event consumer -->

+	<component className="org.apache.felix.ipojo.test.donut.DonutConsumerImpl"

+		name="donut-event-consumer">

+		<!-- Expose the donut consumer service -->

+		<provides interface="org.apache.felix.ipojo.test.donut.DonutConsumer">

+			<property name="name" field="m_name" value="Unknown donut consumer"/>

+			<property name="slow" field="m_isSlow" value="false"/>

+		</provides>

+		<!-- Raw events subscriber -->

+		<ev:subscriber name="donut-event-subscriber" callback="receiveEvent"

+			topics="food/donuts"/>

+	</component>

+	

+	<!-- The event consumer -->

+	<component className="org.apache.felix.ipojo.test.donut.EventConsumerImpl"

+		name="event-consumer">

+		<!-- Expose the donut consumer service -->

+		<provides

+			interface="{org.apache.felix.ipojo.test.donut.DonutConsumer,org.osgi.service.event.EventHandler}">

+			<property name="name" field="m_name" value="Unknown event consumer"/>

+			<property name="slow" field="m_isSlow" value="false"/>

+			<property name="event.topics" type="String" value="food/donuts"/>

+		</provides>

+	</component>

+	

+	<!-- The event tracker -->

+	<component className="org.apache.felix.ipojo.test.donut.EventTrackerImpl"

+		name="event-tracker">

+		<!-- Expose the donut consumer service -->

+		<provides

+			interface="{org.apache.felix.ipojo.test.donut.EventTracker,org.osgi.service.event.EventHandler}">

+			<property name="name" field="m_name" value="Unknown event tracker"/>

+			<property name="event.topics" type="String" value="food/donuts"/>

+		</provides>

+	</component>

+	

+	

+	<!-- Example instances -->

+	<instance component="donut-provider" name="zeifhgbzre">

+		<property name="name" value="zeifhgbzre donuts"/>

+	</instance>

+	<instance component="donut-consumer" name="zeifhgbzre simpson">

+		<property name="name" value="zeifhgbzre simpson"/>

+		<property name="slow" value="false"/>

+	</instance>

+	

+</ipojo>
\ No newline at end of file
diff --git a/ipojo/tests/handler/whiteboard/pom.xml b/ipojo/tests/handler/whiteboard/pom.xml
index 4199193..f328779 100644
--- a/ipojo/tests/handler/whiteboard/pom.xml
+++ b/ipojo/tests/handler/whiteboard/pom.xml
@@ -1,5 +1,5 @@
 <project>

-  <groupId>ipojo.test</groupId>

+  <groupId>ipojo.tests</groupId>

   <version>0.9.0-SNAPSHOT</version>

   <modelVersion>4.0.0</modelVersion>

   <packaging>bundle</packaging>