Moving the EventAdmin from sandbox to trunk (FELIX-19)

git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@397861 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/Activator.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/Activator.java
new file mode 100644
index 0000000..d0e8cdb
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/Activator.java
@@ -0,0 +1,405 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl;
+
+import org.apache.felix.eventadmin.impl.adapter.BundleEventAdapter;
+import org.apache.felix.eventadmin.impl.adapter.FrameworkEventAdapter;
+import org.apache.felix.eventadmin.impl.adapter.LogEventAdapter;
+import org.apache.felix.eventadmin.impl.adapter.ServiceEventAdapter;
+import org.apache.felix.eventadmin.impl.dispatch.CacheThreadPool;
+import org.apache.felix.eventadmin.impl.dispatch.DelayScheduler;
+import org.apache.felix.eventadmin.impl.dispatch.Scheduler;
+import org.apache.felix.eventadmin.impl.dispatch.TaskHandler;
+import org.apache.felix.eventadmin.impl.dispatch.ThreadPool;
+import org.apache.felix.eventadmin.impl.handler.BlacklistingHandlerTasks;
+import org.apache.felix.eventadmin.impl.handler.CacheFilters;
+import org.apache.felix.eventadmin.impl.handler.CacheTopicHandlerFilters;
+import org.apache.felix.eventadmin.impl.handler.CleanBlackList;
+import org.apache.felix.eventadmin.impl.handler.Filters;
+import org.apache.felix.eventadmin.impl.handler.HandlerTasks;
+import org.apache.felix.eventadmin.impl.handler.TopicHandlerFilters;
+import org.apache.felix.eventadmin.impl.security.CacheTopicPermissions;
+import org.apache.felix.eventadmin.impl.security.SecureEventAdminFactory;
+import org.apache.felix.eventadmin.impl.security.TopicPermissions;
+import org.apache.felix.eventadmin.impl.tasks.AsyncDeliverTasks;
+import org.apache.felix.eventadmin.impl.tasks.DeliverTasks;
+import org.apache.felix.eventadmin.impl.tasks.DispatchTask;
+import org.apache.felix.eventadmin.impl.tasks.SyncDeliverTasks;
+import org.apache.felix.eventadmin.impl.util.LeastRecentlyUsedCacheMap;
+import org.apache.felix.eventadmin.impl.util.LogWrapper;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.service.event.TopicPermission;
+
+/**
+ * The activator of the EventAdmin bundle. This class registers an implementation of
+ * the OSGi R4 <tt>EventAdmin</tt> service (see the Compendium 113) with the 
+ * framework. It features timeout-based blacklisting of event-handlers for both, 
+ * asynchronous and synchronous event-dispatching (as a spec conform optional 
+ * extension). 
+ * 
+ * The service knows about the following properties which are read at bundle startup:
+ * 
+ * <p>
+ * <p>
+ *      <tt>org.apache.felix.eventadmin.CacheSize</tt> - The size of various internal 
+ *          caches.
+ * </p>
+ * The default value is 30. Increase in case of a large number (more then 100) of 
+ * <tt>EventHandler</tt> services. A value less then 10 triggers the default value.
+ * </p>    
+ * <p>   
+ * <p>
+ *      <tt>org.apache.felix.eventadmin.ThreadPoolSize</tt> - The size of the thread 
+ *          pool.
+ * </p>
+ * The default value is 10. Increase in case of a large amount of synchronous events 
+ * where the <tt>EventHandler</tt> services in turn send new synchronous events in 
+ * the event dispatching thread or a lot of timeouts are to be expected. A value of 
+ * less then 2 triggers the default value. A value of 2 effectively disables thread 
+ * pooling.
+ * </p>
+ * <p>
+ * <p>
+ *      <tt>org.apache.felix.eventadmin.Timeout</tt> - The black-listing timeout in 
+ *          milliseconds
+ * </p>    
+ * The default value is 5000. Increase or decrease at own discretion. A value of less 
+ * then 100 turns timeouts off. Any other value is the time in milliseconds granted 
+ * to each <tt>EventHandler</tt> before it gets blacklisted. 
+ * </p>
+ * <p>
+ * <p>
+ *      <tt>org.apache.felix.eventadmin.RequireTopic</tt> - Are <tt>EventHandler</tt> 
+ *          required to be registered with a topic?
+ * </p>
+ * The default is <tt>true</tt>. The specification says that <tt>EventHandler</tt> 
+ * must register with a list of topics they are interested in. Setting this value to 
+ * <tt>false</tt> will enable that handlers without a topic are receiving all events
+ * (i.e., they are treated the same as with a topic=*).
+ * </p>
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+// TODO: This is a tricky one: what do we do in case we are stopped but still have
+// already posted or send events that are not yet delivered? At the moment we will
+// throw an IllegalStateException on new events after we are stopped but
+// will deliver the pending events. Now the question is do we block in the stop 
+// method until we don't have pending events anymore or (as we currently do) return
+// immediately and deliver the events ignoring the fact that we are stopped? What is 
+// the best practice? Alternatively, we could just free all blocked threads and 
+// discard any pending events (Note that this would need some refactoring). From my
+// point of view the current approach has the advantage that the semantics are 
+// preserved: First, the immediate return allows other bundles to be notified that we 
+// are stopped (and hence they deserve an IllegalStateException if they call us 
+// regardless) second, successful posted or send events will be delivered, and third
+// we don't block a shutdown due to only using daemon threads internally. However,
+// it might take some time until we settle down which is somewhat cumbersome given
+// that we already are marked as stopped?
+// TODO: Security is in place but untested due to not being implemented by the 
+// framework. However, it needs to be revisited once security is implemented. 
+// Two places are affected by this namely, security/* and handler/*
+public class Activator implements BundleActivator
+{
+    // The thread pool used - this is a member because we need to close it on stop
+    private volatile ThreadPool m_pool;
+    
+    // The asynchronous event queue - this is a member because we need to close it on
+    // stop
+    private volatile TaskHandler m_asyncQueue;
+    
+    // The synchronous event queue - this is a member because we need to close it on
+    // stop
+    private volatile TaskHandler m_syncQueue;
+    
+    // The actual implementation of the service - this is a member because we need to
+    // close it on stop. Note, security is not part of this implementation but is
+    // added via a decorator in the start method (this is the wrapped object without 
+    // the wrapper).
+    private volatile EventAdminImpl m_admin;
+    
+    /**
+     * Called upon starting of the bundle. Constructs and registers the EventAdmin
+     * service with the framework. Note that the properties of the service are 
+     * requested from the context in this method hence, the bundle has to be 
+     * restarted in order to take changed properties into account. 
+     *
+     * @param context The bundle context passed by the framework
+     * 
+     * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+     */
+    public void start(final BundleContext context)
+    { 
+        // init the LogWrapper. Subsequently, the static methods of the LogWrapper 
+        // can be used to log messages similar to the LogService. The effect of a 
+        // call to any of this methods is either a print to standard out (in case
+        // no LogService is present) or a call to the respective method of 
+        // available LogServices (the reason is that this way the bundle is 
+        // independent of the org.osgi.service.log package)
+        LogWrapper.setContext(context);
+        
+        // The size of various internal caches. At the moment there are 4 
+        // internal caches affected. Each will cache the determined amount of 
+        // small but frequently used objects (i.e., in case of the default value
+        // we end-up with a total of 120 small objects being cached). A value of less 
+        // then 10 triggers the default value.
+        final int cacheSize = getIntProperty("org.apache.felix.eventadmin.CacheSize", 
+            context, 30, 10);
+
+        // The size of the internal thread pool. Note that we must execute
+        // each synchronous event dispatch that happens in the synchronous event
+        // dispatching thread in a new thread, hence a small thread pool is o.k.
+        // A value of less then 2 triggers the default value. A value of 2 
+        // effectively disables thread pooling. Furthermore, this will be used by 
+        // a lazy thread pool (i.e., new threads are created when needed). Ones the
+        // the size is reached and no cached thread is available new threads will
+        // be created.
+        final int threadPoolSize = getIntProperty(
+            "org.apache.felix.eventadmin.ThreadPoolSize", context, 10, 2);
+
+        // The timeout in milliseconds - A value of less then 100 turns timeouts off. 
+        // Any other value is the time in milliseconds granted to each EventHandler 
+        // before it gets blacklisted. 
+        final int timeout = getIntProperty("org.apache.felix.eventadmin.Timeout",
+            context, 5000, Integer.MIN_VALUE);
+        
+        // Are EventHandler required to be registered with a topic? - The default is 
+        // true. The specification says that EventHandler must register with a list 
+        // of topics they are interested in. Setting this value to false will enable 
+        // that handlers without a topic are receiving all events
+        // (i.e., they are treated the same as with a topic=*).
+        final boolean requireTopic = getBooleanProperty(
+            "org.apache.felix.eventadmin.RequireTopic", context, true);
+        
+        LogWrapper.getLogger().log(LogWrapper.LOG_DEBUG,
+            "org.apache.felix.eventadmin.CacheSize=" + cacheSize);
+    
+        LogWrapper.getLogger().log(LogWrapper.LOG_DEBUG,
+            "org.apache.felix.eventadmin.ThreadPoolSize=" + threadPoolSize);
+    
+        LogWrapper.getLogger().log(LogWrapper.LOG_DEBUG,
+            "org.apache.felix.eventadmin.Timeout=" + timeout);
+        
+        LogWrapper.getLogger().log(LogWrapper.LOG_DEBUG,
+            "org.apache.felix.eventadmin.RequireTopic=" + requireTopic);
+    
+        final TopicPermissions publishPermissions = new CacheTopicPermissions(
+            new LeastRecentlyUsedCacheMap(cacheSize), TopicPermission.PUBLISH);
+        
+        final TopicPermissions subscribePermissions = new CacheTopicPermissions(
+            new LeastRecentlyUsedCacheMap(cacheSize), TopicPermission.SUBSCRIBE);
+        
+        final TopicHandlerFilters topicHandlerFilters = 
+            new CacheTopicHandlerFilters(new LeastRecentlyUsedCacheMap(cacheSize),
+            requireTopic);
+        
+        final Filters filters = new CacheFilters(
+            new LeastRecentlyUsedCacheMap(cacheSize), context);
+        
+        // The handlerTasks object is responsible to determine concerned EventHandler
+        // for a given event. Additionally, it keeps a list of blacklisted handlers.
+        // Note that blacklisting is deactivated by selecting a different scheduler
+        // below (and not in this HandlerTasks object!)
+        final HandlerTasks handlerTasks = new BlacklistingHandlerTasks(context, 
+            new CleanBlackList(), topicHandlerFilters, filters, 
+            subscribePermissions);
+        
+        // Either we need a scheduler that will trigger EventHandler blacklisting 
+        // (timeout >= 100) or a null object (timeout < 100)
+        final Scheduler scheduler = createScheduler(timeout);
+        
+        // Note that this uses a lazy thread pool that will create new threads on 
+        // demand - in case none of its cached threads is free - until threadPoolSize 
+        // is reached. Subsequently, a threadPoolSize of 2 effectively disables 
+        // caching of threads.
+        m_pool = new CacheThreadPool(threadPoolSize);
+        
+        m_asyncQueue = new TaskHandler();
+        
+        m_syncQueue = new TaskHandler();
+        
+        m_admin = new EventAdminImpl(handlerTasks, 
+            createAsyncExecuters(m_asyncQueue, m_syncQueue, scheduler, m_pool), 
+            createSyncExecuters(m_syncQueue, scheduler, m_pool));
+        
+        // register the admin wrapped in a service factory (SecureEventAdminFactory)
+        // that hands-out the m_admin object wrapped in a decorator that checks 
+        // appropriated permissions of each calling bundle
+        context.registerService(EventAdmin.class.getName(), 
+            new SecureEventAdminFactory(m_admin, publishPermissions), null);
+        
+        // Finally, adapt the outside events to our kind of events as per spec
+        adaptEvents(context, m_admin);
+    }
+
+    /**
+     * Called upon stopping the bundle.
+     *
+     * @param context The bundle context passed by the framework
+     * 
+     * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+     */
+    public void stop(final BundleContext context)
+    {
+        m_admin.stop();
+        
+        m_pool.close();
+        
+        m_asyncQueue.close();
+        
+        m_syncQueue.close();
+  
+        m_admin = null;
+        
+        m_pool = null;
+        
+        m_asyncQueue = null;
+        
+        m_syncQueue = null;
+    }
+    
+    /*
+     * Create an AsyncDeliverTasks object that is used to dispatch asynchronous 
+     * events. Additionally, the asynchronous dispatch queue is initialized and
+     * activated (i.e., a thread is started via the given ThreadPool).
+     */
+    private DeliverTasks createAsyncExecuters(final TaskHandler handler,
+        final TaskHandler handoverHandler, final Scheduler scheduler, 
+        final ThreadPool pool)
+    {
+        // init the queue
+        final AsyncDeliverTasks result = new AsyncDeliverTasks(handler, 
+            handoverHandler, pool);
+
+        // set-up the queue for asynchronous event delivery and activate it
+        // (i.e., a thread is started via the pool)
+        result.execute(new DispatchTask(handler, scheduler, result));
+
+        return result;
+    }
+
+    /*
+     * Create a SyncDeliverTasks object that is used to dispatch synchronous events.
+     * Additionally, the synchronous dispatch queue is initialized and activated
+     * (i.e., a thread is started via the given ThreadPool).
+     */
+    private DeliverTasks createSyncExecuters(final TaskHandler handler,
+        final Scheduler scheduler, final ThreadPool pool)
+    {
+        // init the queue
+        final SyncDeliverTasks result = new SyncDeliverTasks(handler, pool);
+
+        // set-up the queue for synchronous event delivery and activate it 
+        // (i.e. a thread is started via the pool)
+        result.execute(new DispatchTask(handler, scheduler, result));
+
+        return result;
+    }
+    
+    /*
+     * Returns either a new DelayScheduler with a delay of timeout or the 
+     * Scheduler.NULL_SCHEDULER in case timeout is < 100 in which case timeout and
+     * subsequently black-listing is disabled.
+     */
+    private Scheduler createScheduler(final int timeout)
+    {
+        if(100 > timeout)
+        {
+            return Scheduler.NULL_SCHEDULER;
+        }
+        
+        return new DelayScheduler(timeout);
+    }
+    
+    /*
+     * Init the adapters in org.apache.felix.eventadmin.impl.adapter
+     */
+    private void adaptEvents(final BundleContext context, final EventAdmin admin)
+    {
+        new FrameworkEventAdapter(context, admin);
+
+        new BundleEventAdapter(context, admin);
+
+        new ServiceEventAdapter(context, admin);
+
+        new LogEventAdapter(context, admin);
+    }
+    
+    /*
+     * Returns either the parsed int from the value of the property if it is set and
+     * not less then the min value or the default. Additionally, a warning is 
+     * generated in case the value is erroneous (i.e., can not be parsed as an int or
+     * is less then the min value).
+     */
+    private int getIntProperty(final String key, final BundleContext context, 
+        final int defaultValue, final int min)
+    {
+        final String value = context.getProperty(key);
+       
+        if(null != value)
+        {
+            try {
+                final int result = Integer.parseInt(value);
+                
+                if(result >= min)
+                {
+                    return result;
+                }
+                
+                LogWrapper.getLogger().log(LogWrapper.LOG_WARNING, 
+                        "Value for property: " + key + " is to low - Using default");
+            } catch (NumberFormatException e) {
+                LogWrapper.getLogger().log(LogWrapper.LOG_WARNING, 
+                    "Unable to parse property: " + key + " - Using default", e);
+            }
+        }
+        
+        return defaultValue;
+    }
+    
+
+    /*
+     * Returns true if the value of the property is set and is either 1, true, or yes
+     * Returns false if the value of the property is set and is either 0, false, or no
+     * Returns the defaultValue otherwise
+     */
+    private boolean getBooleanProperty(final String key, final BundleContext context, 
+        final boolean defaultValue)
+    {
+        String value = context.getProperty(key);
+        
+        if(null != value)
+        {
+            value = value.trim().toLowerCase();
+            
+            if(0 < value.length() && ("0".equals(value) || "false".equals(value) 
+                || "no".equals(value)))
+            {
+                return false;
+            }
+            
+            if(0 < value.length() && ("1".equals(value) || "true".equals(value)
+                || "yes".equals(value)))
+            {
+                return true;
+            }
+        }
+        
+        return defaultValue;
+    }
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/EventAdminImpl.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/EventAdminImpl.java
new file mode 100644
index 0000000..0e4a23d
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/EventAdminImpl.java
@@ -0,0 +1,162 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl;
+
+import org.apache.felix.eventadmin.impl.handler.HandlerTasks;
+import org.apache.felix.eventadmin.impl.tasks.DeliverTasks;
+import org.apache.felix.eventadmin.impl.tasks.HandlerTask;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+
+/**
+ * This is the actual implementation of the OSGi R4 Event Admin Service (see the 
+ * Compendium 113 for details). The implementation uses a <tt>HandlerTasks</tt>
+ * in order to determine applicable <tt>EventHandler</tt> for a specific event and
+ * subsequently dispatches the event to the handlers via <tt>DeliverTasks</tt>.
+ * To do this, it uses two different <tt>DeliverTasks</tt> one for asynchronous and
+ * one for synchronous event delivery depending on whether its <tt>post()</tt> or
+ * its <tt>send()</tt> method is called. Note that the actual work is done in the 
+ * implementations of the <tt>DeliverTasks</tt>. Additionally, a stop method is
+ * provided that prevents subsequent events to be delivered.
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class EventAdminImpl implements EventAdmin
+{
+    // The factory used to determine applicable EventHandlers - this will be replaced
+    // by a null object in stop() that subsequently throws an IllegalStateException
+    private volatile HandlerTasks m_managers;
+
+    // The asynchronous event dispatcher
+    private final DeliverTasks m_postManager;
+
+    // The synchronous event dispatcher
+    private final DeliverTasks m_sendManager;
+
+    /**
+     * The constructor of the <tt>EventAdmin</tt> implementation. The 
+     * <tt>HandlerTasks</tt> factory is used to determine applicable 
+     * <tt>EventHandler</tt> for a given event. Additionally, the two 
+     * <tt>DeliverTasks</tt> are used to dispatch the event.
+     *  
+     * @param managers The factory used to determine applicable <tt>EventHandler</tt>
+     * @param postManager The asynchronous event dispatcher
+     * @param sendManager The synchronous event dispatcher
+     */
+    public EventAdminImpl(final HandlerTasks managers,
+        final DeliverTasks postManager, final DeliverTasks sendManager)
+    {
+        checkNull(managers, "Managers");
+        checkNull(postManager, "PostManager");
+        checkNull(sendManager, "SendManager");
+        
+        m_managers = managers;
+
+        m_postManager = postManager;
+
+        m_sendManager = sendManager;
+    }
+    
+    /**
+     * Post an asynchronous event.
+     * 
+     * @param event The event to be posted by this service
+     * 
+     * @throws IllegalStateException - In case we are stopped
+     * 
+     * @see org.osgi.service.event.EventAdmin#postEvent(org.osgi.service.event.Event)
+     */
+    public void postEvent(final Event event)
+    {
+        handleEvent(m_managers.createHandlerTasks(event), m_postManager);
+    }
+
+    /**
+     * Send a synchronous event.
+     * 
+     * @param event The event to be send by this service
+     *
+     * @throws IllegalStateException - In case we are stopped
+     * 
+     * @see org.osgi.service.event.EventAdmin#sendEvent(org.osgi.service.event.Event)
+     */
+    public void sendEvent(final Event event)
+    {
+        handleEvent(m_managers.createHandlerTasks(event), m_sendManager);
+    }
+
+    /**
+     * This method can be used to stop the delivery of events. The m_managers is 
+     * replaced with a null object that throws an IllegalStateException on a call
+     * to <tt>createHandlerTasks()</tt>.
+     */
+    public void stop()
+    {
+        // replace the HandlerTasks with a null object that will throw an 
+        // IllegalStateException on a call to createHandlerTasks
+        m_managers = new HandlerTasks()
+        {
+            /**
+             * This is a null object and this method will throw an 
+             * IllegalStateException due to the bundle being stopped.
+             * 
+             * @param event An event that is not used.
+             * 
+             * @return This method does not return normally
+             * 
+             * @throws IllegalStateException - This is a null object and this method
+             *          will always throw an IllegalStateException
+             */
+            public HandlerTask[] createHandlerTasks(final Event event)
+            {
+                throw new IllegalStateException("The EventAdmin is stopped");
+            }
+        };
+    }
+    
+    /*
+     * This is a utility method that uses the given DeliverTasks to create a 
+     * dispatch tasks that subsequently is used to dispatch the given HandlerTasks.
+     */
+    private void handleEvent(final HandlerTask[] managers,
+        final DeliverTasks manager)
+    {
+        if (0 < managers.length)
+        {
+            // This might throw an IllegalStateException in case that we are stopped
+            // and the null object for m_managers was not fast enough established
+            // This is needed in the adapter/* classes due to them sending
+            // events whenever they receive an event from their source.
+            // Service importers that call us regardless of the fact that we are
+            // stopped deserve an exception anyways
+            manager.createTask().execute(managers);
+        }
+    }
+    
+    /*
+     * This is a utility method that will throw a <tt>NullPointerException</tt>
+     * in case that the given object is null. The message will be of the form 
+     * "${name} + may not be null".
+     */
+    private void checkNull(final Object object, final String name)
+    {
+        if(null == object)
+        {
+            throw new NullPointerException(name + " may not be null");
+        }
+    }
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/adapter/BundleEventAdapter.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/adapter/BundleEventAdapter.java
new file mode 100644
index 0000000..c40da41
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/adapter/BundleEventAdapter.java
@@ -0,0 +1,120 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.adapter;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleListener;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.service.event.EventConstants;
+
+/**
+ * This class registers itself as a listener for bundle events and posts them via
+ * the EventAdmin as specified in 113.6.4 OSGi R4 compendium.
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class BundleEventAdapter implements BundleListener
+{
+    private final EventAdmin m_admin;
+    
+    /**
+     * The constructor of the adapter. This will register the adapter with the given
+     * context as a <tt>BundleListener</tt> and subsequently, will post received 
+     * events via the given EventAdmin.
+     * 
+     * @param context The bundle context with which to register as a listener.
+     * @param admin The <tt>EventAdmin</tt> to use for posting events.
+     */
+    public BundleEventAdapter(final BundleContext context, final EventAdmin admin)
+    {
+        if(null == admin)
+        {
+            throw new NullPointerException("EventAdmin must not be null");
+        }
+        
+        m_admin = admin;
+        
+        context.addBundleListener(this);
+    }
+    
+    /**
+     * Once a bundle event is received this method assembles and posts an event via
+     * the <tt>EventAdmin</tt> as specified in 113.6.4 OSGi R4 compendium. 
+     * 
+     * @param event The event to adapt.
+     */
+    public void bundleChanged(final BundleEvent event)
+    {
+        final Dictionary properties = new Hashtable();
+
+        properties.put(EventConstants.EVENT, event);
+
+        properties.put("bundle.id", new Long(event.getBundle()
+            .getBundleId()));
+
+        final String symbolicName = event.getBundle().getSymbolicName();
+
+        if (null != symbolicName)
+        {
+            properties.put(EventConstants.BUNDLE_SYMBOLICNAME,
+                symbolicName);
+        }
+
+        properties.put("bundle", event.getBundle());
+
+        final StringBuffer topic = new StringBuffer(BundleEvent.class
+            .getName().replace('.', '/')).append('/');
+
+        switch (event.getType())
+        {
+            case BundleEvent.INSTALLED:
+                topic.append("INSTALLED");
+                break;
+            case BundleEvent.STARTED:
+                topic.append("STARTED");
+                break;
+            case BundleEvent.STOPPED:
+                topic.append("STOPPED");
+                break;
+            case BundleEvent.UPDATED:
+                topic.append("UPDATED");
+                break;
+            case BundleEvent.UNINSTALLED:
+                topic.append("UNINSTALLED");
+                break;
+            case BundleEvent.RESOLVED:
+                topic.append("RESOLVED");
+                break;
+            case BundleEvent.UNRESOLVED:
+                topic.append("UNRESOLVED");
+                break;
+            default:
+                return; // IGNORE EVENT
+        }
+
+        try {
+            m_admin.postEvent(new Event(topic.toString(), properties));
+        } catch (IllegalStateException e) { 
+            // This is o.k. - indicates that we are stopped.
+        }
+    }
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/adapter/FrameworkEventAdapter.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/adapter/FrameworkEventAdapter.java
new file mode 100644
index 0000000..3e0e926
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/adapter/FrameworkEventAdapter.java
@@ -0,0 +1,141 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.adapter;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.service.event.EventConstants;
+
+/**
+ * This class registers itself as a listener for framework events and posts them via
+ * the EventAdmin as specified in 113.6.3 OSGi R4 compendium.
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class FrameworkEventAdapter implements FrameworkListener
+{
+    private final EventAdmin m_admin;
+    
+    /**
+     * The constructor of the adapter. This will register the adapter with the
+     * given context as a <tt>FrameworkListener</tt> and subsequently, will
+     * post received events via the given EventAdmin.
+     * 
+     * @param context The bundle context with which to register as a listener.
+     * @param admin The <tt>EventAdmin</tt> to use for posting events.
+     */
+    public FrameworkEventAdapter(final BundleContext context, final EventAdmin admin)
+    {
+        if(null == admin)
+        {
+            throw new NullPointerException("EventAdmin must not be null");
+        }
+        
+        m_admin = admin;
+        
+        context.addFrameworkListener(this);
+    }
+    
+    /**
+     * Once a framework event is received this method assembles and posts an event 
+     * via the <tt>EventAdmin</tt> as specified in 113.6.3 OSGi R4 compendium. 
+     * 
+     * @param event The event to adapt.
+     */
+    public void frameworkEvent(final FrameworkEvent event)
+    {
+        final Dictionary properties = new Hashtable();
+
+        properties.put(EventConstants.EVENT, event);
+
+        final Bundle bundle = event.getBundle();
+
+        if (null != bundle)
+        {
+            properties.put("bundle.id", new Long(bundle.getBundleId()));
+
+            final String symbolicName = bundle.getSymbolicName();
+
+            if (null != symbolicName)
+            {
+                properties.put(EventConstants.BUNDLE_SYMBOLICNAME,
+                    symbolicName);
+            }
+
+            properties.put("bundle", bundle);
+        }
+
+        final Throwable thrown = event.getThrowable();
+
+        if (null != thrown)
+        {
+            properties.put(EventConstants.EXCEPTION_CLASS, 
+                thrown.getClass().getName());
+
+            final String message = thrown.getMessage();
+
+            if (null != message)
+            {
+                properties.put(EventConstants.EXCEPTION_MESSAGE,
+                    message);
+            }
+
+            properties.put(EventConstants.EXCEPTION, thrown);
+        }
+
+        final StringBuffer topic = new StringBuffer(
+            FrameworkEvent.class.getName().replace('.', '/'))
+            .append('/');
+
+        switch (event.getType())
+        {
+            case FrameworkEvent.STARTED:
+                topic.append("STARTED");
+                break;
+            case FrameworkEvent.ERROR:
+                topic.append("ERROR");
+                break;
+            case FrameworkEvent.PACKAGES_REFRESHED:
+                topic.append("PACKAGES_REFRESHED");
+                break;
+            case FrameworkEvent.STARTLEVEL_CHANGED:
+                topic.append("STARTLEVEL_CHANGED");
+                break;
+            case FrameworkEvent.WARNING:
+                topic.append("WARNING");
+                break;
+            case FrameworkEvent.INFO:
+                topic.append("INFO");
+                break;
+            default:
+                return; // IGNORE EVENT
+        }
+
+        try {
+            m_admin.postEvent(new Event(topic.toString(), properties));
+        } catch(IllegalStateException e) {
+            // This is o.k. - indicates that we are stopped.
+        }
+    }
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/adapter/LogEventAdapter.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/adapter/LogEventAdapter.java
new file mode 100644
index 0000000..4d98e8e
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/adapter/LogEventAdapter.java
@@ -0,0 +1,293 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.adapter;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.felix.eventadmin.impl.util.LogWrapper;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.service.event.EventConstants;
+
+/**
+ * This class registers itself as a listener for <tt>LogReaderService</tt> services
+ * with the framework and subsequently, a <tt>LogListener</tt> callback with any
+ * currently available <tt>LogReaderService</tt>. Any received log event is then 
+ * posted via the EventAdmin as specified in 113.6.6 OSGi R4 compendium.
+ * Note that this class does not create a hard dependency on the org.osgi.service.log
+ * packages. The adaption only takes place if it is present or once it becomes 
+ * available hence, combined with a DynamicImport-Package no hard dependency is 
+ * needed. 
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class LogEventAdapter implements ServiceListener
+{
+    // The internal lock for this object used instead synchronized(this)
+    private final Object m_lock = new Object();
+    
+    private BundleContext m_context;
+    
+    // A singleton instance of the used log listener that is the adapter
+    private Object m_logListener;
+    
+    final EventAdmin m_admin;
+    
+    /**
+     * The constructor of the adapter. This will register the adapter with the
+     * given context as a listener for <tt>LogReaderService</tt> services and 
+     * subsequently, a <tt>LogListener</tt> callback with any currently available
+     * <tt>LogReaderService</tt>. Any received log event is then posted via the given 
+     * EventAdmin.
+     * 
+     * @param context The bundle context with which to register as a listener.
+     * @param admin The <tt>EventAdmin</tt> to use for posting events.
+     */
+    public LogEventAdapter(final BundleContext context, final EventAdmin admin)
+    {
+        if(null == admin)
+        {
+            throw new NullPointerException("EventAdmin must not be null");
+        }
+        
+        m_context = context;
+        
+        m_admin = admin;
+
+        try
+        {
+            m_context.addServiceListener(this, "(" + Constants.OBJECTCLASS
+                + "=org.osgi.service.log.LogReaderService)");
+
+            final ServiceReference[] refs;
+
+            refs = m_context.getServiceReferences(
+                "org.osgi.service.log.LogReaderService", null);
+
+            if (null != refs)
+            {
+                for (int i = 0; i < refs.length; i++)
+                {
+                    final org.osgi.service.log.LogReaderService logReader = 
+                        (org.osgi.service.log.LogReaderService) m_context
+                        .getService(refs[i]);
+
+                    if (null != logReader)
+                    {
+                        logReader.addLogListener((org.osgi.service.log.LogListener) 
+                            getLogListener());
+                    }
+                }
+            }
+        } catch (InvalidSyntaxException e)
+        {
+            // This never happens
+        }
+    }
+
+    /**
+     * Once a <tt>LogReaderService</tt> register event is received this method
+     * registers a <tt>LogListener</tt> with the received service that assembles 
+     * and posts any log event via the <tt>EventAdmin</tt> as specified in 
+     * 113.6.6 OSGi R4 compendium. 
+     * 
+     * @param event The event to adapt.
+     */
+    public void serviceChanged(final ServiceEvent event)
+    {
+        if (ServiceEvent.REGISTERED == event.getType())
+        {
+            final org.osgi.service.log.LogReaderService logReader = 
+                (org.osgi.service.log.LogReaderService) m_context
+                .getService(event.getServiceReference());
+
+            if (null != logReader)
+            {
+                logReader.addLogListener((org.osgi.service.log.LogListener) 
+                    getLogListener());
+            }
+        }
+    }
+
+    /*
+     * Constructs a LogListener that assembles and posts any log event via the 
+     * EventAdmin as specified in 113.6.6 OSGi R4 compendium. Note that great
+     * care is taken to not create a hard dependency on the org.osgi.service.log
+     * package.
+     */
+    private Object getLogListener()
+    {
+        synchronized (m_lock)
+        {
+            if (null != m_logListener)
+            {
+                return m_logListener;
+            }
+
+            m_logListener = new org.osgi.service.log.LogListener()
+            {
+                public void logged(final org.osgi.service.log.LogEntry entry)
+                {
+                    // This is where the assembly as specified in 133.6.6 OSGi R4
+                    // compendium is taking place (i.e., the log entry is adapted to 
+                    // an event and posted via the EventAdmin)
+                    
+                    final Dictionary properties = new Hashtable();
+
+                    final Bundle bundle = entry.getBundle();
+
+                    if (null != bundle)
+                    {
+                        properties.put("bundle.id", new Long(bundle
+                            .getBundleId()));
+
+                        final String symbolicName = bundle.getSymbolicName();
+
+                        if (null != symbolicName)
+                        {
+                            properties.put(EventConstants.BUNDLE_SYMBOLICNAME,
+                                symbolicName);
+                        }
+
+                        properties.put("bundle", bundle);
+                    }
+
+                    properties.put("log.level", new Integer(entry.getLevel()));
+
+                    properties.put(EventConstants.MESSAGE, entry.getMessage());
+
+                    properties.put(EventConstants.TIMESTAMP, new Long(
+                        entry.getTime()));
+
+                    properties.put("log.entry", entry);
+
+                    final Throwable exception = entry.getException();
+
+                    if (null != exception)
+                    {
+                        properties.put(EventConstants.EXCEPTION_CLASS,
+                            exception.getClass().getName());
+
+                        final String message = exception.getMessage();
+
+                        if (null != message)
+                        {
+                            properties.put(EventConstants.EXCEPTION_MESSAGE,
+                                message);
+                        }
+
+                        properties.put(EventConstants.EXCEPTION, exception);
+                    }
+
+                    final ServiceReference service = entry
+                        .getServiceReference();
+
+                    if (null != service)
+                    {
+                        properties.put(EventConstants.SERVICE, service);
+
+                        final Object id = service
+                            .getProperty(EventConstants.SERVICE_ID);
+
+                        if (null != id)
+                        {
+                            try
+                            {
+                                properties.put(EventConstants.SERVICE_ID,
+                                    new Long(id.toString()));
+                            } catch (NumberFormatException ne)
+                            {
+                                // LOG and IGNORE
+                                LogWrapper.getLogger().log(
+                                    entry.getServiceReference(),
+                                    LogWrapper.LOG_WARNING, "Exception parsing " + 
+                                    EventConstants.SERVICE_ID + "=" + id, ne);
+                            }
+                        }
+
+                        final Object pid = service.getProperty(
+                            EventConstants.SERVICE_PID);
+
+                        if (null != pid)
+                        {
+                            properties.put(EventConstants.SERVICE_PID, 
+                                pid.toString());
+                        }
+
+                        final Object objectClass = service.getProperty(
+                            Constants.OBJECTCLASS);
+
+                        if (null != objectClass)
+                        {
+                            if (objectClass instanceof String[])
+                            {
+                                properties.put(
+                                    EventConstants.SERVICE_OBJECTCLASS,
+                                    objectClass);
+                            }
+                            else
+                            {
+                                properties.put(
+                                    EventConstants.SERVICE_OBJECTCLASS,
+                                    new String[] { objectClass.toString() });
+                            }
+                        }
+                    }
+
+                    final StringBuffer topic = new StringBuffer(
+                        org.osgi.service.log.LogEntry.class.getName().replace(
+                            '.', '/')).append('/');
+
+                    switch (entry.getLevel())
+                    {
+                        case org.osgi.service.log.LogService.LOG_ERROR:
+                            topic.append("LOG_ERROR");
+                            break;
+                        case org.osgi.service.log.LogService.LOG_WARNING:
+                            topic.append("LOG_WARNING");
+                            break;
+                        case org.osgi.service.log.LogService.LOG_INFO:
+                            topic.append("LOG_INFO");
+                            break;
+                        case org.osgi.service.log.LogService.LOG_DEBUG:
+                            topic.append("LOG_DEBUG");
+                            break;
+                        default:
+                            topic.append("LOG_OTHER");
+                            break;
+                    }
+
+                    try {
+                        m_admin.postEvent(new Event(topic.toString(), properties));
+                    } catch(IllegalStateException e) {
+                        // This is o.k. - indicates that we are stopped.
+                    }
+                }
+            };
+            
+            return m_logListener;
+        }
+    }
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/adapter/ServiceEventAdapter.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/adapter/ServiceEventAdapter.java
new file mode 100644
index 0000000..bd3336f
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/adapter/ServiceEventAdapter.java
@@ -0,0 +1,138 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.adapter;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.felix.eventadmin.impl.util.LogWrapper;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.service.event.EventConstants;
+
+/**
+ * This class registers itself as a listener for service events and posts them via
+ * the EventAdmin as specified in 113.6.5 OSGi R4 compendium.
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class ServiceEventAdapter implements ServiceListener
+{
+    private final EventAdmin m_admin;
+    
+    /**
+     * The constructor of the adapter. This will register the adapter with the
+     * given context as a <tt>ServiceListener</tt> and subsequently, will
+     * post received events via the given EventAdmin.
+     * 
+     * @param context The bundle context with which to register as a listener.
+     * @param admin The <tt>EventAdmin</tt> to use for posting events.
+     */
+    public ServiceEventAdapter(final BundleContext context, final EventAdmin admin)
+    {
+        m_admin = admin;
+        
+        context.addServiceListener(this);
+    }
+
+    /**
+     * Once a Service event is received this method assembles and posts an event 
+     * via the <tt>EventAdmin</tt> as specified in 113.6.5 OSGi R4 compendium. 
+     * 
+     * @param event The event to adapt.
+     */
+    public void serviceChanged(final ServiceEvent event)
+    {
+        final Dictionary properties = new Hashtable();
+
+        properties.put(EventConstants.EVENT, event);
+
+        properties.put(EventConstants.SERVICE, event
+            .getServiceReference());
+
+        final Object id = event.getServiceReference().getProperty(
+            EventConstants.SERVICE_ID);
+
+        if (null != id)
+        {
+            try
+            {
+                properties.put(EventConstants.SERVICE_ID, new Long(id
+                    .toString()));
+            } catch (NumberFormatException ne)
+            {
+                // LOG and IGNORE
+                LogWrapper.getLogger().log(event.getServiceReference(),
+                    LogWrapper.LOG_WARNING, "Exception parsing " + 
+                    EventConstants.SERVICE_ID + "=" + id, ne);
+            }
+        }
+
+        final Object pid = event.getServiceReference().getProperty(
+            EventConstants.SERVICE_PID);
+
+        if (null != pid)
+        {
+            properties.put(EventConstants.SERVICE_PID, pid.toString());
+        }
+
+        final Object objectClass = event.getServiceReference()
+            .getProperty(Constants.OBJECTCLASS);
+
+        if (null != objectClass)
+        {
+            if (objectClass instanceof String[])
+            {
+                properties.put(EventConstants.SERVICE_OBJECTCLASS,
+                    objectClass);
+            }
+            else
+            {
+                properties.put(EventConstants.SERVICE_OBJECTCLASS,
+                    new String[] { objectClass.toString() });
+            }
+        }
+
+        final StringBuffer topic = new StringBuffer(ServiceEvent.class
+            .getName().replace('.', '/')).append('/');
+
+        switch (event.getType())
+        {
+            case ServiceEvent.REGISTERED:
+                topic.append("REGISTERED");
+                break;
+            case ServiceEvent.MODIFIED:
+                topic.append("MODIFIED");
+                break;
+            case ServiceEvent.UNREGISTERING:
+                topic.append("UNREGISTERING");
+                break;
+            default:
+                return; // IGNORE
+        }
+
+        try {
+            m_admin.postEvent(new Event(topic.toString(), properties));
+        } catch(IllegalStateException e) {
+            // This is o.k. - indicates that we are stopped.
+        }
+    }
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/dispatch/CacheThreadPool.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/dispatch/CacheThreadPool.java
new file mode 100644
index 0000000..fd6e779
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/dispatch/CacheThreadPool.java
@@ -0,0 +1,360 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.dispatch;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.felix.eventadmin.impl.tasks.DeliverTask;
+import org.apache.felix.eventadmin.impl.tasks.DispatchTask;
+
+/**
+ * An implementation of a thread pool that uses a fixed number of cached threads
+ * but will spin-off new threads as needed. The underlying assumption is that
+ * threads that have been created more recently will be available sooner then older
+ * threads hence, once the pool size is reached older threads will be decoupled from
+ * the pool and the newly created are added to it.
+ *  
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+// TODO: The least recently used method deployed is rather a hack in this case
+//      it really should be refactored into a plugable strategy. However, I believe 
+//      it to be the best strategy in this case.
+public class CacheThreadPool implements ThreadPool
+{
+    // The internal lock for this object used instead synchronized(this)
+    // Note that it is used by the pooled threads created by this pool too. This is
+    // the reason why it is not private. Don't use it from the outside.
+    final Object m_lock = new Object();
+    
+    // The pooled threads
+    private final PooledThread[] m_pool;
+    
+    // The least recently used index
+    private final List m_index;
+    
+    // Is this pool closed i.e., do we not pool thread anymore?
+    private boolean m_closed = false;
+    
+    /**
+     * The constructor of the pool. The given size will be used as the max number of
+     * pooled threads. 
+     * 
+     * @param size The max number of threads pooled at a given time.
+     */
+    public CacheThreadPool(final int size)
+    {
+        synchronized (m_lock)
+        {
+            m_pool = new PooledThread[size];
+            
+            // We assume that a list is expanded once it reaches half of its capacity
+            // and it doesn't harm if the assumption is wrong.
+            m_index = new ArrayList(size + 1 + (size / 2));
+        }
+    }
+    
+    /**
+     * Executes the task in a thread out of the pool or a new thread if no pooled 
+     * thread is available. In case that the max size is reached the least recently
+     * used (i.e., the longest executing) thread in the pool is decoupled and a new 
+     * one added to the pool that is used to execute the task.
+     * 
+     * @param task The task to execute
+     * @param callback The callback associated with the task
+     * 
+     * @see org.apache.felix.eventadmin.impl.dispatch.ThreadPool#execute(DispatchTask, DeliverTask)
+     */
+    public void execute(final DispatchTask task, final DeliverTask callback)
+    {
+        // Note that we associate a callback with a task via the thread used to 
+        // execute the task. In general, a free slot in the pool (i.e., m_pool[i] is 
+        // null) can be used to set-up a new thread. Also note that we need to 
+        // update the LRU index if we change the pool.
+        synchronized(m_lock)
+        {
+            if(m_closed)
+            {
+                // We are closed hence, spin-of a new thread for the new task.
+                final PooledThread result = new PooledThread();
+                
+                // Set-up the thread and associate the task with the callback.
+                result.reset(task, callback);
+                
+                // release the thread immediately since we don't pool anymore.
+                result.release();
+                
+                return;
+            }
+            
+            // Search in the pool for a free thread.
+            for (int i = 0; i < m_pool.length; i++)
+            {
+                // o.k. we found a free slot now set-up a new thread for it.
+                if (null == m_pool[i])
+                {
+                    m_pool[i] = new PooledThread();
+
+                    m_pool[i].reset(task, callback);
+                    
+                    m_index.add(new Integer(i));
+                    
+                    return;
+                }
+                else if (m_pool[i].available())
+                {
+                    // we found a free thread now set it up.
+                    m_pool[i].reset(task, callback);
+                    
+                    final Integer idx = new Integer(i);
+                    
+                    m_index.remove(idx);
+                    
+                    m_index.add(idx);
+                    
+                    return;
+                }
+            }
+
+            // The pool is full and no threads are available hence, spin-off a new
+            // thread and add it to the pool while decoupling the least recently used
+            // one. This assumes that older threads are likely to take longer to 
+            // become available again then younger ones.
+            final int pos = ((Integer) m_index.remove(0)).intValue();
+            
+            m_index.add(new Integer(pos));
+            
+            m_pool[pos].release();
+
+            m_pool[pos] = new PooledThread();
+
+            m_pool[pos].reset(task, callback);
+        }
+    }
+
+    /**
+     * Look-up the callback associated with the task that the given thread is 
+     * currently executing or return the default value that may be <tt>null</tt>.
+     * 
+     * @param thread The thread that is currently executing the task for which to 
+     *      return the callback. In case the thread is not created by an instance of
+     *      this class the default value will be returned.
+     * @param defaultCallback The value to return in case that the thread was not
+     *      created by an instance of this class. May be <tt>null</tt> 
+     * @return The callback associated with the given thread or the default value.
+     * 
+     * @see org.apache.felix.eventadmin.impl.dispatch.ThreadPool#getCallback(Thread, DeliverTask)
+     */
+    public DeliverTask getCallback(final Thread thread, final DeliverTask defaultCallback)
+    {
+        synchronized (m_lock)
+        {
+            if (thread instanceof PooledThread)
+            {
+                return ((PooledThread) thread).getCallback();
+            }
+
+            return defaultCallback;
+        }
+    }
+
+    /**
+     * Look-up the task that the given thread is currently executing or return the
+     * default value that may be <tt>null</tt> in case that the thread has not been
+     * created by an instance of this class.
+     * 
+     * @param thread The thread whose currently executed task should be returned.
+     * @param defaultTask The default value to be returned in case that the thread
+     *      was not created by this instance or doesn't currently has a task. May be
+     *      <tt>null</tt>
+     * @return The task the given thread is currently executing or the defaultTask
+     * 
+     * @see org.apache.felix.eventadmin.impl.dispatch.ThreadPool#getTask(Thread, DispatchTask)
+     */
+    public DispatchTask getTask(Thread thread, DispatchTask defaultTask)
+    {
+        synchronized (m_lock)
+        {
+            if (thread instanceof PooledThread)
+            {
+                return ((PooledThread) thread).getTask();
+            }
+
+            return defaultTask;
+        }
+    }
+
+    /**
+     * Close the pool i.e, stop pooling threads. Note that subsequently, task will
+     * still be executed but no pooling is taking place anymore.
+     * 
+     * @see org.apache.felix.eventadmin.impl.dispatch.ThreadPool#close()
+     */
+    public void close()
+    {
+        synchronized (m_lock)
+        {
+            // We are closed hence, decouple all threads from the pool
+            for (int i = 0; i < m_pool.length; i++)
+            {
+                if (null != m_pool[i])
+                {
+                    m_pool[i].release();
+                    
+                    m_pool[i] = null;
+                }
+            }
+            
+            m_closed = true;
+        }
+    }
+    
+    /*
+     * The threads created by this pool. A PooledThread blocks until it gets a new
+     * task from the pool or is released. Additionally, it is used to associate 
+     * the task it currently runs with its callback.
+     */
+    private class PooledThread extends Thread
+    {
+        // The current task or null if none
+        private DispatchTask m_runnable = null;
+
+        // The callback associated with the current task
+        private DeliverTask m_callback = null;
+
+        // Is this thread decoupled from the pool (i.e, may cease to exists once its
+        // current task is finished)?
+        private boolean m_released = false;
+
+        /*
+         * This will set-up the thread as a daemon and start it too. No need to call
+         * its start method explicitly
+         */
+        PooledThread()
+        {
+            setDaemon(true);
+
+            start();
+        }
+
+        
+        /**
+         * Call next() in a loop until next() returns null indicating that we are
+         * done (i.e., decoupled from the pool) and may cease to exist.
+         */
+        public void run()
+        {
+            for (Runnable next = next(); null != next; next = next())
+            {
+                next.run();
+
+                synchronized (m_lock)
+                {
+                    m_runnable = null;
+                }
+            }
+        }
+
+        /*
+         * Block until a new task is available or we are decoupled from the pool.
+         * This will return the next task or null if we are decoupled from the pool
+         */
+        private DispatchTask next()
+        {
+            synchronized (m_lock)
+            {
+                while (null == m_runnable)
+                {
+                    if (m_released)
+                    {
+                        return null;
+                    }
+
+                    try
+                    {
+                        m_lock.wait();
+                    } catch (InterruptedException e)
+                    {
+                        // Not needed
+                    }
+                }
+
+                return m_runnable;
+            }
+        }
+
+        /*
+         * Set-up the thread for the next task
+         */
+        void reset(final DispatchTask next, final DeliverTask callback)
+        {
+            synchronized (m_lock)
+            {
+                m_runnable = next;
+                m_callback = callback;
+                m_lock.notifyAll();
+            }
+        }
+
+        /*
+         * Return the callback associated with the current task
+         */
+        DeliverTask getCallback()
+        {
+            synchronized (m_lock)
+            {
+                return m_callback;
+            }
+        }
+
+        /*
+         * Return whether this thread is available (i.e., has no task and has not 
+         * been released) or not.
+         */
+        boolean available()
+        {
+            synchronized (m_lock)
+            {
+                return (null == m_runnable) && (!m_released);
+            }
+        }
+
+        /*
+         * Return the current task or null if none
+         */
+        DispatchTask getTask()
+        {
+            synchronized (m_lock)
+            {
+                return m_runnable;
+            }
+        }
+
+        /*
+         * Decouple this thread from the pool
+         */
+        void release()
+        {
+            synchronized (m_lock)
+            {
+                m_released = true;
+
+                m_lock.notifyAll();
+            }
+        }
+    }
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/dispatch/DelayScheduler.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/dispatch/DelayScheduler.java
new file mode 100644
index 0000000..866c207
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/dispatch/DelayScheduler.java
@@ -0,0 +1,98 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.dispatch;
+
+import java.util.Date;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * A simple delay scheduler that schedules tasks based on a fixed delay. Possible
+ * nice values are subtracted from this delay where appropriate. Note that this
+ * class uses a <tt>java.util.Timer</tt> internally that is set to be a daemon hence,
+ * allows to shutdown the vm regardless but can not be stopped. The spec says that 
+ * a <tt>java.util.Timer</tt> without a reference to itself should go away eventually
+ * but no guaranties are given. It follows that once the bundle is stopped all 
+ * references to instances of this class should be released and this in turn will 
+ * allow that the timer thread goes away eventually, but this may take an arbitrary 
+ * amount of time.
+ * 
+ * @see org.apache.felix.eventadmin.impl.dispatch.Scheduler
+ * @see java.util.Timer
+ * 
+ *  @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class DelayScheduler implements Scheduler
+{
+    // The timer used for scheduling. Note that it will not be stopped by but 
+    // by the vm once all references to this instance are gone (at least eventually).
+    private final Timer m_timer = new Timer(true);
+
+    private final int m_delay;
+
+    /**
+     * The constructor of the scheduler. The scheduler will use the given delay to 
+     * schedule tasks accordingly.
+     * 
+     * @param delay The delay in milliseconds before a task is executed
+     */
+    public DelayScheduler(final int delay)
+    {
+        m_delay = delay;
+    }
+    
+    /**
+     * Schedule the task to execute after the given delay.
+     *
+     * @param task The task to schedule for execution.
+     * 
+     * @see org.apache.felix.eventadmin.impl.dispatch.Scheduler#schedule(java.lang.Runnable)
+     */
+    public void schedule(final Runnable task)
+    {
+        scheduleTaskWithDelay(task, m_delay);
+    }
+
+    /**
+     * Schedule the task to execute after the given delay minus the nice.
+     * 
+     * @param task The task to schedule for execution after delay minus nice
+     * @param nice The time to subtract from the delay.
+     * 
+     * @see org.apache.felix.eventadmin.impl.dispatch.Scheduler#schedule(java.lang.Runnable, int)
+     */
+    public void schedule(final Runnable task, int nice)
+    {
+        scheduleTaskWithDelay(task, m_delay - nice);
+    }
+    
+    /*
+     * This method creates a new TimerTask as a wrapper around the given task
+     * and calls the m_timer.schedule method with it and the current time plus the 
+     * delay.
+     */
+    private void scheduleTaskWithDelay(final Runnable task, final int delay)
+    {
+        m_timer.schedule(new TimerTask()
+        {
+            public void run()
+            {
+                task.run();
+            }
+        }, new Date(System.currentTimeMillis() + delay));
+    }
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/dispatch/Scheduler.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/dispatch/Scheduler.java
new file mode 100644
index 0000000..8bf918b
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/dispatch/Scheduler.java
@@ -0,0 +1,76 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.dispatch;
+
+/**
+ * A simple scheduler that accepts a task and schedules its for execution at 
+ * its own discretion (i.e., the behavior of the actual implementor). The only 
+ * possible hint is a nice value that should be subtracted from any fixed scheduling 
+ * interval. Additionally, a null object is provided that can be used to disable 
+ * scheduled execution. 
+ *  
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public interface Scheduler
+{
+    /**
+     * This is a null object that can be used in case no scheduling is needed. In 
+     * other words tasks given to this scheduler are never executed.
+     */
+    public final Scheduler NULL_SCHEDULER = new Scheduler(){
+        /**
+         * This is a null object hence, this method does nothing.
+         * 
+         * @param task A task that will never be run.
+         */
+        public void schedule(final Runnable task)
+        {
+            // This is a null object hence we don't do nothing.
+        }
+        
+        /**
+         * This is a null object hence, this method does nothing.
+         * 
+         * @param task A task that will never be run.
+         * @param nice A nice value that will never be used.
+         */
+        public void schedule(final Runnable task, final int nice)
+        {
+            // This is a null object hence we don't do nothing.
+        }
+    };
+    
+    /**
+     * Schedule the given task for execution at a later time based on the behavior
+     * of the actual implementor of this interface. Note that this may mean that
+     * the task is never executed.
+     * 
+     * @param task The task to schedule for execution.
+     */
+    public void schedule(final Runnable task);
+    
+    /**
+     * Schedule the given task for execution at a later time based on the behavior
+     * of the actual implementor of this interface. Note that this may mean that
+     * the task is never executed. The nice value should be subtracted from any fixed 
+     * scheduling interval.
+     * 
+     * @param task The task to schedule for execution.
+     * @param nice A value to subtract from any fixed scheduling interval.
+     */
+    public void schedule(final Runnable task, final int nice);
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/dispatch/TaskHandler.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/dispatch/TaskHandler.java
new file mode 100644
index 0000000..b3f3a04
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/dispatch/TaskHandler.java
@@ -0,0 +1,151 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.dispatch;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.felix.eventadmin.impl.tasks.HandlerTask;
+
+/**
+ * This class implements the <tt>TaskQueue</tt> and the <tt>TaskProducer</tt> 
+ * interface. It makes the tasks added via the queue interface available via the
+ * producer interface until the queue is closed and the producer returns 
+ * <tt>null</tt>. 
+ * 
+ * @see org.apache.felix.eventadmin.impl.dispatch.TaskQueue
+ * @see org.apache.felix.eventadmin.impl.dispatch.TaskProducer
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class TaskHandler implements TaskQueue, TaskProducer
+{
+    // The queue that is used as a lock as well
+    private final List m_queue = new ArrayList();
+
+    // Are we closed?
+    private boolean m_closed = false;
+    
+    /**
+     * Append the tasks to this queue in one atomic operation while preserving their
+     * order.
+     * 
+     * @param tasks The tasks to append to this queue
+     * 
+     * @throws IllegalStateException in case that this queue is already closed
+     * 
+     * @see org.apache.felix.eventadmin.impl.dispatch.TaskQueue#append(HandlerTask[])
+     */
+    public void append(final HandlerTask[] tasks)
+    {
+        synchronized (m_queue)
+        {
+            if(m_closed)
+            {
+                throw new IllegalArgumentException("Queue is closed");
+            }
+            
+            for (int i = 0; i < tasks.length; i++)
+            {
+                m_queue.add(tasks[i]);
+            }
+            
+            if(!m_queue.isEmpty())
+            {
+                m_queue.notifyAll();
+            }
+        }
+    }
+
+    /**
+     * Push the tasks to this queue in one atomic operation while preserving their
+     * order.  
+     * 
+     * @param tasks The tasks to push to the front of this queue.
+     * 
+     * @throws IllegalStateException in case that this queue is already closed
+     * 
+     * @see org.apache.felix.eventadmin.impl.dispatch.TaskQueue#push(HandlerTask[])
+     */
+    public void push(final HandlerTask[] tasks)
+    {
+        synchronized (m_queue)
+        {
+            if(m_closed)
+            {
+                throw new IllegalArgumentException("Queue is closed");
+            }
+            
+            for (int i = tasks.length -1; i >= 0; i--)
+            {
+                m_queue.add(0, tasks[i]);
+            }
+            
+            if(!m_queue.isEmpty())
+            {
+                m_queue.notifyAll();
+            }
+        }
+    }
+
+    /**
+     * Close the queue. 
+     * 
+     * @see org.apache.felix.eventadmin.impl.dispatch.TaskQueue#close()
+     */
+    public void close()
+    {
+        synchronized(m_queue)
+        {
+            m_closed = true;
+            
+            m_queue.notifyAll();
+        }
+    }
+    
+    /**
+     * Block until a new task is ready and is returned or no more tasks will be 
+     * returned.
+     * 
+     * @return The next task or <tt>null</tt> if no more tasks will be produced
+     * 
+     * @see org.apache.felix.eventadmin.impl.dispatch.TaskProducer#next()
+     */
+    public HandlerTask next()
+    {
+        synchronized (m_queue)
+        {
+            while(!m_closed && m_queue.isEmpty())
+            {
+                try
+                {
+                    m_queue.wait();
+                } catch (InterruptedException e)
+                {
+                    // Not needed
+                }
+            }
+            
+            if(!m_queue.isEmpty())
+            {
+                return (HandlerTask) m_queue.remove(0);
+            }
+            
+            return null;
+        }
+    }
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/dispatch/TaskProducer.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/dispatch/TaskProducer.java
new file mode 100644
index 0000000..f1c8acb
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/dispatch/TaskProducer.java
@@ -0,0 +1,37 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.dispatch;
+
+import org.apache.felix.eventadmin.impl.tasks.HandlerTask;
+
+/**
+ * Instances of this interface will deliver new tasks as soon as they are available
+ * while blocking in the <tt>next()</tt> call until then. Unless there won't be any
+ * more tasks in which case <tt>null</tt> is returned.
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public interface TaskProducer
+{
+    /**
+     * Block until a new task is ready and is returned or no more tasks will be 
+     * returned.
+     * 
+     * @return The next task or <tt>null</tt> if no more tasks will be produced
+     */
+    public HandlerTask next();
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/dispatch/TaskQueue.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/dispatch/TaskQueue.java
new file mode 100644
index 0000000..c75a692
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/dispatch/TaskQueue.java
@@ -0,0 +1,55 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.dispatch;
+
+import org.apache.felix.eventadmin.impl.tasks.HandlerTask;
+
+/**
+ * This is the interface for a simple queue that allows to append or push arrays
+ * of tasks to it. The elements of such an array are added atomically (i.e, they
+ * are in the same order one after the other in the queue) either at the end or the
+ * front of the queue. Additionally, the queue can be closed. 
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public interface TaskQueue
+{
+    /**
+     * Append the tasks to this queue in one atomic operation while preserving their
+     * order.
+     * 
+     * @param tasks The tasks to append to this queue
+     * 
+     * @throws IllegalStateException in case that this queue is already closed
+     */
+    public void append(HandlerTask[] tasks);
+    
+    /**
+     * Push the tasks to this queue in one atomic operation while preserving their
+     * order.  
+     * 
+     * @param tasks The tasks to push to the front of this queue.
+     * 
+     * @throws IllegalStateException in case that this queue is already closed
+     */
+    public void push(HandlerTask[] tasks);
+    
+    /**
+     * Close the queue. 
+     */
+    public void close();
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/dispatch/ThreadPool.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/dispatch/ThreadPool.java
new file mode 100644
index 0000000..b496fbf
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/dispatch/ThreadPool.java
@@ -0,0 +1,78 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.dispatch;
+
+import org.apache.felix.eventadmin.impl.tasks.DeliverTask;
+import org.apache.felix.eventadmin.impl.tasks.DispatchTask;
+
+/**
+ * A ThreadPool interface that allows to execute tasks using pooled threads in order
+ * to ease the thread creation overhead and additionally, to associate a callback
+ * with the thread that executes the task. Subsequently, the callback for a given 
+ * thread can be asked from instances of this class. Finally, the currently executed
+ * task of a thread created by this pool can be retrieved as well. The look-up
+ * methods accept plain thread objects and will return given default values in case
+ * that the specific threads have not been created by this pool. Note that a closed
+ * pool should still execute new tasks but stop pooling threads. 
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public interface ThreadPool
+{
+    /**
+     * Execute the task in a free thread or create a new one. The given callback 
+     * will be associated with the executing thread as long as it is executed. 
+     * 
+     * @param task The task to execute
+     * @param callback The callback that will be associated with the executing thread
+     *      or <tt>null</tt> if none.
+     */
+    public void execute(final DispatchTask task, final DeliverTask callback);
+    
+    /**
+     * Look-up the callback associated with the task that the given thread is 
+     * currently executing or return the default value that may be <tt>null</tt>.
+     * 
+     * @param thread The thread that is currently executing the task for which to 
+     *      return the callback. In case the thread is not created by an instance of
+     *      this class the default value will be returned.
+     * @param defaultCallback The value to return in case that the thread was not
+     *      created by an instance of this class. May be <tt>null</tt> 
+     * @return The callback associated with the given thread or the default value.
+     */
+    public DeliverTask getCallback(final Thread thread, 
+        final DeliverTask defaultCallback);
+    
+    /**
+     * Look-up the task that the given thread is currently executing or return the
+     * default value that may be <tt>null</tt> in case that the thread has not been
+     * created by an instance of this class.
+     * 
+     * @param thread The thread whose currently executed task should be returned.
+     * @param defaultTask The default value to be returned in case that the thread
+     *      was not created by this instance or doesn't currently has a task. May be
+     *      <tt>null</tt>
+     * @return The task the given thread is currently executing or the defaultTask
+     */
+    public DispatchTask getTask(final Thread thread, final DispatchTask defaultTask);
+
+    /**
+     * Close the pool i.e, stop pooling threads. Note that subsequently, task will
+     * still be executed but no pooling is taking place anymore.
+     */
+    public void close();
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/BlackList.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/BlackList.java
new file mode 100644
index 0000000..3a11bf7
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/BlackList.java
@@ -0,0 +1,47 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.handler;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * This interface represents a simple set that allows to add service references
+ * and lookup whether a given reference is in the list. Note that implementations
+ * of this interface may do additional service reference life-cycle related
+ * clean-up actions like removing references that point to unregistered services.
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public interface BlackList
+{
+    /**
+     * Add a service to this blacklist.
+     * 
+     * @param ref The reference of the service that is blacklisted
+     */
+    public void add(final ServiceReference ref);
+    
+    /**
+     * Lookup whether a given service is blacklisted.
+     * 
+     * @param ref The reference of the service
+     * 
+     * @return <tt>true</tt> in case that the service reference has been blacklisted, 
+     *      <tt>false</tt> otherwise.
+     */
+    public boolean contains(final ServiceReference ref);
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/BlacklistingHandlerTasks.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/BlacklistingHandlerTasks.java
new file mode 100644
index 0000000..9f50b3a
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/BlacklistingHandlerTasks.java
@@ -0,0 +1,227 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.handler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.felix.eventadmin.impl.security.TopicPermissions;
+import org.apache.felix.eventadmin.impl.tasks.HandlerTask;
+import org.apache.felix.eventadmin.impl.tasks.HandlerTaskImpl;
+import org.apache.felix.eventadmin.impl.util.LogWrapper;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventConstants;
+import org.osgi.service.event.EventHandler;
+
+/**
+ * This class is an implementation of the HandlerTasks interface that does provide
+ * blacklisting of event handlers. Furthermore, handlers are determined from the
+ * framework on any call to <tt>createHandlerTasks()</tt> hence, there is no 
+ * book-keeping of <tt>EventHandler</tt> services while they come and go but a 
+ * query for each sent event. In order to do this, an ldap-filter is created that
+ * will match applicable <tt>EventHandler</tt> references. In order to ease some of 
+ * the overhead pains of this approach some light caching is going on.
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class BlacklistingHandlerTasks implements HandlerTasks
+{
+    // The blacklist that holds blacklisted event handler service references
+    private final BlackList m_blackList;
+
+    // The context of the bundle used to get the actual event handler services
+    private final BundleContext m_context;
+
+    // Used to create the filters that can determine applicable event handlers for
+    // a given event
+    private final TopicHandlerFilters m_topicHandlerFilters;
+
+    // Used to create the filters that are used to determine whether an applicable
+    // event handler is interested in a particular event
+    private final Filters m_filters;
+
+    // Used to create and possibly cache topic permissions
+    private final TopicPermissions m_topicPermissions;
+
+    /**
+     * The constructor of the factory. 
+     * 
+     * @param context The context of the bundle
+     * @param blackList The set to use for keeping track of blacklisted references
+     * @param topicHandlerFilters The factory for topic handler filters
+     * @param filters The factory for <tt>Filter</tt> objects
+     * @param topicPermissions The factory for permission objects of type PUBLISH
+     */
+    public BlacklistingHandlerTasks(final BundleContext context,
+        final BlackList blackList,
+        final TopicHandlerFilters topicHandlerFilters, final Filters filters,
+        final TopicPermissions topicPermissions)
+    {
+        checkNull(context, "Context");
+        checkNull(blackList, "BlackList");
+        checkNull(topicHandlerFilters, "TopicHandlerFilters");
+        checkNull(filters, "Filters");
+        checkNull(topicPermissions, "TopicPermissions");
+        
+        m_context = context;
+
+        m_blackList = blackList;
+
+        m_topicHandlerFilters = topicHandlerFilters;
+
+        m_filters = filters;
+
+        m_topicPermissions = topicPermissions;
+    }
+
+    /**
+     * Create the handler tasks for the event. All matching event handlers are
+     * determined and delivery tasks for them returned.
+     * 
+     * @param event The event for which' handlers delivery tasks must be created
+     * 
+     * @return A delivery task for each handler that matches the given event
+     *
+     * @see org.apache.felix.eventadmin.impl.handler.HandlerTasks#createHandlerTasks(org.osgi.service.event.Event)
+     */
+    public HandlerTask[] createHandlerTasks(final Event event)
+    {
+        final List result = new ArrayList();
+
+        ServiceReference[] handlerRefs = new ServiceReference[0];
+
+        try
+        {
+            handlerRefs = m_context.getServiceReferences(EventHandler.class
+                .getName(), m_topicHandlerFilters.createFilterForTopic(event
+                .getTopic()));
+        } catch (InvalidSyntaxException e)
+        {
+            LogWrapper.getLogger().log(LogWrapper.LOG_WARNING,
+                "Invalid EVENT_TOPIC [" + event.getTopic() + "]", e);
+        }
+        
+        if(null == handlerRefs)
+        {
+            handlerRefs = new ServiceReference[0];
+        }
+        
+        for (int i = 0; i < handlerRefs.length; i++)
+        {
+            if (!m_blackList.contains(handlerRefs[i])
+                && handlerRefs[i].getBundle().hasPermission(
+                    m_topicPermissions.createTopicPermission(event.getTopic())))
+            {
+                try
+                {
+                    if (event.matches(m_filters.createFilter(
+                        (String) handlerRefs[i]
+                            .getProperty(EventConstants.EVENT_FILTER),
+                        Filters.TRUE_FILTER)))
+                    {
+                        result.add(new HandlerTaskImpl(handlerRefs[i],
+                            event, this));
+                    }
+                } catch (InvalidSyntaxException e)
+                {
+                    LogWrapper.getLogger().log(
+                        handlerRefs[i],
+                        LogWrapper.LOG_WARNING,
+                        "Invalid EVENT_FILTER - Blacklisting ServiceReference ["
+                            + handlerRefs[i] + " | Bundle("
+                            + handlerRefs[i].getBundle() + ")]", e);
+
+                    m_blackList.add(handlerRefs[i]);
+                }
+            }
+        }
+
+        return (HandlerTaskImpl[]) result
+            .toArray(new HandlerTaskImpl[result.size()]);
+    }
+
+    /**
+     * Blacklist the given service reference. This is a private method and only
+     * public due to its usage in a friend class.
+     * 
+     * @param handlerRef The service reference to blacklist
+     */
+    public void blackList(final ServiceReference handlerRef)
+    {
+        m_blackList.add(handlerRef);
+        
+        LogWrapper.getLogger().log(
+            LogWrapper.LOG_WARNING,
+            "Blacklisting ServiceReference [" + handlerRef + " | Bundle("
+                + handlerRef.getBundle() + ")] due to timeout!");
+    }
+
+    /**
+     * Get the real EventHandler service for the handlerRef from the context in case
+     * the ref is not blacklisted and the service is not unregistered. The
+     * NullEventHandler object is returned otherwise. This is a private method and
+     * only public due to its usage in a friend class.
+     * 
+     * @param handlerRef The service reference for which to get its service
+     * @return The service of the reference or a null object if the service is
+     *      unregistered
+     */
+    public EventHandler getEventHandler(final ServiceReference handlerRef)
+    {
+        final Object result = (m_blackList.contains(handlerRef)) ? null
+            : m_context.getService(handlerRef);
+
+        return (EventHandler) ((null != result) ? result : m_nullEventHandler);
+    }
+
+    /*
+     * This is a null object that is supposed to do nothing. This is used once an 
+     * EventHandler is requested for a service reference that is either stale
+     * (i.e., unregistered) or blacklisted
+     */
+    private final EventHandler m_nullEventHandler = new EventHandler()
+    {
+        /**
+         * This is a null object that is supposed to do nothing at this point.
+         * 
+         * @param event an event that is not used
+         */
+        public void handleEvent(final Event event)
+        {
+            // This is a null object that is supposed to do nothing at this
+            // point. This is used once a EventHandler is requested for a 
+            // servicereference that is either stale (i.e., unregistered) or
+            // blacklisted.
+        }
+    };
+    
+    /*
+     * This is a utility method that will throw a <tt>NullPointerException</tt>
+     * in case that the given object is null. The message will be of the form name +
+     * may not be null.
+     */
+    private void checkNull(final Object object, final String name)
+    {
+        if(null == object)
+        {
+            throw new NullPointerException(name + " may not be null");
+        }
+    }
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/CacheFilters.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/CacheFilters.java
new file mode 100644
index 0000000..883feb2
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/CacheFilters.java
@@ -0,0 +1,92 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.handler;
+
+import org.apache.felix.eventadmin.impl.util.CacheMap;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * This is an implementation of the <tt>Filters</tt> factory that uses a cache in
+ * order to speed-up filter creation.
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class CacheFilters implements Filters
+{
+    // The cache to use
+    private final CacheMap m_cache;
+
+    // The context of the bundle used to create the Filter objects
+    private final BundleContext m_context;
+
+    /**
+     * The constructor of this factory. The cache is used to speed-up filter 
+     * creation.
+     * 
+     * @param cache The cache to use
+     * @param context The context of the bundle used to create the <tt>Filter</tt>
+     *      objects
+     */
+    public CacheFilters(final CacheMap cache, final BundleContext context)
+    {
+        if(null == cache)
+        {
+            throw new NullPointerException("Cache may not be null");
+        }
+        
+        if(null == context)
+        {
+            throw new NullPointerException("Context may not be null");
+        }
+        
+        m_cache = cache;
+
+        m_context = context;
+    }
+
+    /**
+     * Create a filter for the given filter string or return the nullFilter in case
+     * the string is <tt>null</tt>.
+     * 
+     * @param filter The filter as a string
+     * @param nullFilter The default value to return if filter is <tt>null</tt>
+     * @return The <tt>Filter</tt> of the filter string or the nullFilter if the 
+     *      filter string was <tt>null</tt>
+     * @throws InvalidSyntaxException if <tt>BundleContext.createFilter()</tt>
+     *      throws an <tt>InvalidSyntaxException</tt>
+     *      
+     * @see org.apache.felix.eventadmin.impl.handler.Filters#createFilter(java.lang.String, org.osgi.framework.Filter)
+     */
+    public Filter createFilter(String filter, Filter nullFilter)
+        throws InvalidSyntaxException
+    {
+        Filter result = (Filter) ((null != filter) ? m_cache.get(filter)
+            : nullFilter);
+        
+        if (null == result)
+        {
+            result = m_context.createFilter(filter);
+
+            m_cache.add(filter, result);
+        }
+        
+        return result;
+    }
+
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/CacheTopicHandlerFilters.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/CacheTopicHandlerFilters.java
new file mode 100644
index 0000000..de30ac2
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/CacheTopicHandlerFilters.java
@@ -0,0 +1,111 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.handler;
+
+import org.apache.felix.eventadmin.impl.util.CacheMap;
+import org.osgi.service.event.EventConstants;
+
+/**
+ * The factory for <tt>EventHandler</tt> filters based on a certain topic. This
+ * implementation uses a cache to speed-up filter creation.
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class CacheTopicHandlerFilters implements TopicHandlerFilters
+{
+    // The cache
+    private final CacheMap m_cache;
+    
+    private final char[] m_keyChars = EventConstants.EVENT_TOPIC.toCharArray();
+    
+    private final char[] m_filterStart;
+    
+    /**
+     * The constructor of the filter factory. 
+     * 
+     * @param cache The cache to use in order to speed-up filter creation.
+     * 
+     * @param requireTopic Include handlers that do not provide a topic
+     */
+    public CacheTopicHandlerFilters(final CacheMap cache, final boolean requireTopic)
+    {
+        if(null == cache)
+        {
+            throw new NullPointerException("Cache may not be null");
+        }
+        
+        m_cache = cache;
+        
+        m_filterStart = ("(|" + 
+            ((requireTopic) ? "" : "(!(" + new String(m_keyChars) + "=*))") +
+            "(" + new String(m_keyChars) + "=\\*)(" + new String(m_keyChars) + 
+            "=").toCharArray();
+    }
+    
+    /**
+     * Create a filter that will match all <tt>EventHandler</tt> services that match
+     * the given topic.
+     * 
+     * @param topic The topic to match
+     * 
+     * @return A filter that will match all <tt>EventHandler</tt> services for 
+     *      the given topic.
+     *      
+     * @see org.apache.felix.eventadmin.impl.handler.TopicHandlerFilters#createFilterForTopic(java.lang.String)
+     */
+    public String createFilterForTopic(String topic)
+    {
+        // build the ldap-query - as a simple example: 
+        // topic=org/apache/felix/TEST
+        // result = (|(topic=\*)(topic=org/\*)(topic=org/apache/\*)
+        //            (topic=org/apache/felix/\*)(topic=org/apache/felix/TEST))
+        String result = (String) m_cache.get(topic);
+        
+        if(null == result)
+        {
+            char[] topicChars = topic.toCharArray();
+
+            final StringBuffer filter = new StringBuffer(topicChars.length
+                * topicChars.length);
+
+            filter.append(m_filterStart);
+
+            for (int i = 0; i < topicChars.length; i++)
+            {
+                if ('/' == topicChars[i])
+                {
+                    filter.append('/').append('\\').append('*').append(')');
+
+                    filter.append('(').append(m_keyChars).append('=').append(
+                        topicChars, 0, i + 1);
+                }
+                else
+                {
+                    filter.append(topicChars[i]);
+                }
+            }
+
+            filter.append(')').append(')');
+
+            result = filter.toString();
+            
+            m_cache.add(topic, result);
+        }
+        
+        return result;
+    }
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/CleanBlackList.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/CleanBlackList.java
new file mode 100644
index 0000000..1c103ad
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/CleanBlackList.java
@@ -0,0 +1,82 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.handler;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * This class implements a <tt>BlackList</tt> that removes references to unregistered
+ * services automatically.
+ * 
+ * @see org.apache.felix.eventadmin.impl.handler.BlackList
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class CleanBlackList implements BlackList
+{
+    // This set removes stale (i.e., unregistered) references on any call to contains
+    private final Set m_blackList = Collections.synchronizedSet(new HashSet()
+        {
+            public boolean contains(final Object object)
+            {
+                for (Iterator iter = super.iterator(); iter.hasNext();)
+                {
+                    final ServiceReference ref = (ServiceReference) iter.next();
+
+                    if (null == ref.getBundle())
+                    {
+                        iter.remove();
+                    }
+                }
+
+                return super.contains(object);
+            }
+        });
+
+    /**
+     * Add a service to this blacklist.
+     * 
+     * @param ref The reference of the service that is blacklisted
+     * 
+     * @see org.apache.felix.eventadmin.impl.handler.BlackList#add(org.osgi.framework.ServiceReference)
+     */
+    public void add(final ServiceReference ref)
+    {
+        m_blackList.add(ref);
+    }
+
+    /**
+     * Lookup whether a given service is blacklisted.
+     * 
+     * @param ref The reference of the service
+     * 
+     * @return <tt>true</tt> in case that the service reference has been blacklisted, 
+     *      <tt>false</tt> otherwise.
+     * 
+     * @see org.apache.felix.eventadmin.impl.handler.BlackList#contains(org.osgi.framework.ServiceReference)
+     */
+    public boolean contains(final ServiceReference ref)
+    {
+        return m_blackList.contains(ref);
+    }
+
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/Filters.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/Filters.java
new file mode 100644
index 0000000..fea183f
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/Filters.java
@@ -0,0 +1,127 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.handler;
+
+import java.util.Dictionary;
+
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * The factory for <tt>Filter</tt> objects. Additionally, two null filter objects
+ * are provided that either always return <tt>true</tt> or <tt>false</tt>,
+ * respectively.
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public interface Filters
+{
+    /**
+     * A null filter object that matches any given service reference.
+     */
+    public static final Filter TRUE_FILTER = new Filter()
+    {
+
+        /**
+         * This is a null object that always returns <tt>true</tt>.
+         * 
+         * @param reference An unused service reference
+         * @return <tt>true</tt>
+         */
+        public boolean match(final ServiceReference reference)
+        {
+            return true;
+        }
+
+        /**
+         * This is a null object that always returns <tt>true</tt>.
+         * 
+         * @param dictionary An unused dictionary
+         * @return <tt>true</tt>
+         */
+        public boolean match(final Dictionary dictionary)
+        {
+            return true;
+        }
+
+        /**
+         * This is a null object that always returns <tt>true</tt>.
+         * 
+         * @param dictionary An unused dictionary.
+         * @return <tt>true</tt>
+         */
+        public boolean matchCase(final Dictionary dictionary)
+        {
+            return true;
+        }
+    };
+
+    /**
+     * A null filter object that does not match any given service reference. 
+     */
+    public static final Filter FALSE_FILTER = new Filter()
+    {
+
+        /**
+         * This is a null object that always returns <tt>false</tt>.
+         * 
+         * @param reference An unused reference.
+         * @return <tt>false</tt>
+         */
+        public boolean match(final ServiceReference reference)
+        {
+           return false;
+        }
+
+        /**
+         * This is a null object that always returns <tt>false</tt>.
+         * 
+         * @param dictionary An unused dictionary
+         * @return <tt>false</tt>
+         */
+        public boolean match(final Dictionary dictionary)
+        {
+            return false;
+        }
+
+        /**
+         * This is a null object that always returns <tt>false</tt>.
+         * 
+         * @param dictionary An unused dictionary.
+         * @return <tt>false</tt>
+         */
+        public boolean matchCase(final Dictionary dictionary)
+        {
+            return false;
+        }
+    };
+
+    /**
+     * Create a filter for the given filter string or return the nullFilter in case
+     * the string is <tt>null</tt>.
+     * 
+     * @param filter The filter as a string
+     * @param nullFilter The default value to return if filter is <tt>null</tt>
+     * @return The <tt>Filter</tt> of the filter string or the nullFilter if the 
+     *      filter string was null
+     * @throws InvalidSyntaxException if <tt>BundleContext.createFilter()</tt>
+     *      throws an <tt>InvalidSyntaxException</tt>
+     */
+    public Filter createFilter(final String filter, final Filter nullFilter)
+        throws InvalidSyntaxException;
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/HandlerTasks.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/HandlerTasks.java
new file mode 100644
index 0000000..f9e2fd2
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/HandlerTasks.java
@@ -0,0 +1,39 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.handler;
+
+import org.apache.felix.eventadmin.impl.tasks.HandlerTask;
+import org.osgi.service.event.Event;
+
+/**
+ * The factory for event handler tasks. Implementations of this interface can be
+ * used to create tasks that handle the delivery of events to event handlers.
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public interface HandlerTasks
+{
+    /**
+     * Create the handler tasks for the event. All matching event handlers must
+     * be determined and delivery tasks for them returned.
+     * 
+     * @param event The event for which' handlers delivery tasks must be created
+     * 
+     * @return A delivery task for each handler that matches the given event
+     */
+    public HandlerTask[] createHandlerTasks(final Event event);
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/TopicHandlerFilters.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/TopicHandlerFilters.java
new file mode 100644
index 0000000..0177185
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/TopicHandlerFilters.java
@@ -0,0 +1,36 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.handler;
+
+/**
+ * The factory for <tt>EventHandler</tt> filters based on a certain topic.
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public interface TopicHandlerFilters
+{
+    /**
+     * Create a filter that will match all <tt>EventHandler</tt> services that match
+     * the given topic.
+     * 
+     * @param topic The topic to match
+     * 
+     * @return A filter that will match all <tt>EventHandler</tt> services for 
+     *      the given topic.
+     */
+    public String createFilterForTopic(final String topic);
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/security/CacheTopicPermissions.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/security/CacheTopicPermissions.java
new file mode 100644
index 0000000..8e6620d
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/security/CacheTopicPermissions.java
@@ -0,0 +1,143 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.security;
+
+import org.apache.felix.eventadmin.impl.util.CacheMap;
+
+/**
+ * An implementation of the <tt>TopicPermissions</tt> factory that uses a given
+ * cache in order to speed-up topic permission creation. Note that a 
+ * <tt>java.lang.Object</tt> is returned in case creating a new TopicPermission 
+ * fails. This assumes that Bundle.hasPermission is used in order to evaluate the
+ * created Permission which in turn will return true if security is not supported
+ * by the framework. Otherwise, it will return false due to receiving something that
+ * is not a subclass of <tt>java.lang.SecurityPermission</tt> hence, this combination
+ * ensures that access is granted in case a topic permission could not be created due
+ * to missing security support by the framework. 
+ *  
+ * @see org.apache.felix.eventadmin.impl.security.TopicPermissions
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class CacheTopicPermissions implements TopicPermissions
+{
+    // The cache used
+    private final CacheMap m_cache;
+    
+    // The type of the permissions created
+    private final String m_type;
+    
+    /**
+     * The constructor of this permission factory. The given cache will be used to
+     * speed-up permission creation and the created permissions will be of the given
+     * type (i.e., PUBLISH or SUBSCRIBE).
+     * 
+     * @param cache The cache to be used
+     * @param type The type that created permissions will be of (i.e, PUBLISH or
+     *      SUBSCRIBE)
+     *      
+     * @see org.apache.felix.eventadmin.impl.security.TopicPermissions
+     * @see org.osgi.service.event.TopicPermission#PUBLISH
+     * @see org.osgi.service.event.TopicPermission#SUBSCRIBE
+     */
+    public CacheTopicPermissions(final CacheMap cache, final String type)
+    {
+        checkNull(cache, "CacheMap");
+        checkNull(type, "Type");
+       
+        if(!org.osgi.service.event.TopicPermission.PUBLISH.equals(type) && 
+            !org.osgi.service.event.TopicPermission.SUBSCRIBE.equals(type))
+        {
+            throw new IllegalArgumentException(
+                "Type must be either PUBLISH or SUBSCRIBE");
+        }
+        
+        m_cache = cache;
+        
+        m_type = type;
+    }
+
+    /**
+     * Returns the type of the permissions created by this factory.
+     * 
+     * @return The type of the permissions created by this factory
+     * 
+     * @see org.apache.felix.eventadmin.impl.security.TopicPermissions#getType()
+     * @see org.osgi.service.event.TopicPermission#PUBLISH
+     * @see org.osgi.service.event.TopicPermission#SUBSCRIBE
+     */
+    public String getType()
+    {
+        return m_type;
+    }
+
+    /**
+     * Creates a <tt>TopicPermission</tt> for the given topic and the type of this
+     * factory (i.e., PUBLISH or SUBSCRIBE). Note that a 
+     * <tt>java.lang.Object</tt> is returned in case creating a new TopicPermission 
+     * fails. This assumes that Bundle.hasPermission is used in order to evaluate the
+     * created Permission which in turn will return true if security is not supported
+     * by the framework. Otherwise, it will return false due to receiving something 
+     * that is not a subclass of <tt>java.lang.SecurityPermission</tt> hence, this 
+     * combination ensures that access is granted in case a topic permission could 
+     * not be created due to missing security support by the framework.
+     * 
+     * @param topic The target topic
+     * 
+     * @return The created permission or a <tt>java.lang.Object</tt> in case the
+     *      permission could not be created.
+     *      
+     * @see org.apache.felix.eventadmin.impl.security.TopicPermissions#createTopicPermission(String)
+     * @see org.osgi.service.event.TopicPermission
+     */
+    public Object createTopicPermission(final String topic)
+    {
+        Object result = m_cache.get(topic);
+        
+        if(null == result)
+        {
+            try
+            {
+                result = new org.osgi.service.event.TopicPermission(topic, m_type);
+            } catch (Throwable t)
+            {
+                // This might happen in case security is not supported
+                // Bundle.hasPermission will return true in this case
+                // hence topicPermission = new Object() is o.k.
+                
+                result = new Object();
+            }
+            
+            m_cache.add(topic, result);
+        }
+        
+        return result;
+    }
+    
+    /*
+     * This is a utility method that will throw a <tt>NullPointerException</tt>
+     * in case that the given object is null. The message will be of the form name +
+     * may not be null.
+     */
+    private void checkNull(final Object object, final String name)
+    {
+        if(null == object)
+        {
+            throw new NullPointerException(name + " may not be null");
+        }
+    }
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/security/EventAdminSecurityDecorator.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/security/EventAdminSecurityDecorator.java
new file mode 100644
index 0000000..543f1c8
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/security/EventAdminSecurityDecorator.java
@@ -0,0 +1,164 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.security;
+
+import org.osgi.framework.Bundle;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+
+/**
+ * This class is a decorator for an <tt>EventAdmin</tt> service. It secures the 
+ * service by checking any call from a given bundle (i.e., the caller) to the admins
+ * post or send methods for the appropriate permissions based on a given permission
+ * factory. This methods then in turn throw a <tt>SecurityException</tt> in case
+ * the given bundle doesn't pass the check or delegate the call to decorated service
+ * instance, respectively.
+ *  
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class EventAdminSecurityDecorator implements EventAdmin
+{
+    // The bundle used to determine appropriate permissions
+    private final Bundle m_bundle;
+    
+    // The decorated service instance
+    private final EventAdmin m_admin;
+    
+    // The permission factory
+    private final TopicPermissions m_topicPermissions;
+    
+    /**
+     * The constructor of this decorator. The given bundle and permission factory 
+     * will be used to determine appropriate permissions for any call to 
+     * <tt>postEvent()</tt> or <tt>sendEvent()</tt>, respectively. This method then
+     * in turn throw a <tt>SecurityException</tt> in case the given bundle doesn't 
+     * pass the check. 
+     * 
+     * @param bundle The calling bundle used to determine appropriate permissions
+     * @param admin The decorated service instance
+     * @param topicPermissions The permission factory 
+     */
+    public EventAdminSecurityDecorator(final Bundle bundle, final EventAdmin admin, 
+        final TopicPermissions topicPermissions)
+    {
+        checkNull(bundle, "Bundle"); 
+        checkNull(admin, "Admin");
+        checkNull(topicPermissions, "TopicPermissions");
+        
+        m_bundle = bundle;
+        
+        m_admin = admin;
+        
+        m_topicPermissions = topicPermissions;
+    }
+    
+    /**
+     * This method checks whether the given (i.e., calling) bundle has 
+     * appropriate permissions to post an event to the targeted topic. A 
+     * <tt>SecurityException</tt> is thrown in case it has not. Otherwise, the 
+     * event is posted using this decorator's service instance.
+     * 
+     * @param event The event that should be posted
+     * 
+     * @see org.osgi.service.event.EventAdmin#postEvent(org.osgi.service.event.Event)
+     */
+    public void postEvent(final Event event)
+    {
+        checkPermission(event.getTopic());
+        
+        m_admin.postEvent(event);
+    }
+
+    /**
+     * This method checks whether the given (i.e., calling) bundle has 
+     * appropriate permissions to send an event to the targeted topic. A
+     * <tt>SecurityException</tt> is thrown in case it has not. Otherwise,
+     * the event is posted using this decorator's service instance.
+     * 
+     * @param event The event that should be send
+     * 
+     * @see org.osgi.service.event.EventAdmin#sendEvent(org.osgi.service.event.Event)
+     */
+    public void sendEvent(final Event event)
+    {
+        checkPermission(event.getTopic());
+        
+        m_admin.sendEvent(event);
+    }
+
+    /**
+     * Overrides <tt>hashCode()</tt> and returns the hash code of the decorated 
+     * service instance.
+     * 
+     * @return The hash code of the decorated service instance
+     * 
+     * @see java.lang.Object#hashCode()
+     * @see org.osgi.service.event.EventAdmin
+     */
+    public int hashCode()
+    {
+        return m_admin.hashCode();
+    }
+    
+    /**
+     * Overrides <tt>equals()</tt> and delegates the call to the decorated service 
+     * instance. In case that o is an instance of this class it passes o's service
+     * instance instead of o.
+     * 
+     * @param o The object to compare with this decorator's service instance
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     * @see org.osgi.service.event.EventAdmin
+     */
+    public boolean equals(final Object o)
+    {
+        if(o instanceof EventAdminSecurityDecorator)
+        {
+            return m_admin.equals(((EventAdminSecurityDecorator) o).m_admin);
+        }
+        
+        return m_admin.equals(o);
+    }
+    
+    /*
+     * This is a utility method that will throw a <tt>SecurityExcepiton</tt> in case
+     * that the given bundle (i.e, the caller) has not appropriate permissions to 
+     * publish to this topic. This method uses Bundle.hasPermission() and the given
+     * permission factory to determine this. 
+     */
+    private void checkPermission(final String topic)
+    {
+        if(!m_bundle.hasPermission(m_topicPermissions.createTopicPermission(topic)))
+        {
+            throw new SecurityException("Bundle[" + m_bundle + 
+                "] has no PUBLISH permission for topic [" + topic + "]");
+        }
+    }
+    
+    /*
+     * This is a utility method that will throw a <tt>NullPointerException</tt>
+     * in case that the given object is null. The message will be of the form name +
+     * may not be null.
+     */
+    private void checkNull(final Object object, final String name)
+    {
+        if(null == object)
+        {
+            throw new NullPointerException(name + " may not be null");
+        }
+    }
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/security/SecureEventAdminFactory.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/security/SecureEventAdminFactory.java
new file mode 100644
index 0000000..c8c50e2
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/security/SecureEventAdminFactory.java
@@ -0,0 +1,109 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.security;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.event.EventAdmin;
+
+/**
+ * This class is a factory that secures a given <tt>EventAdmin</tt> service by
+ * wrapping it with a new instance of an <tt>EventAdminSecurityDecorator</tt> on
+ * any call to its <tt>getService()</tt> method. The decorator will determine the 
+ * appropriate permissions by using the given permission factory and the bundle 
+ * parameter passed to the <tt>getService()</tt> method. 
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class SecureEventAdminFactory implements ServiceFactory
+{
+    // The EventAdmin to secure
+    private EventAdmin m_admin;
+    
+    // The permission factory
+    private final TopicPermissions m_topicPermissions;
+    
+    /**
+     * The constructor of the factory. The factory will use the given event admin and
+     * permission factory to create a new <tt>EventAdminSecurityDecorator</tt>
+     * on any call to <tt>getService()</tt>. 
+     * 
+     * @param admin The <tt>EventAdmin</tt> service to secure.
+     * @param topicPermissions The permission factory to use for permission lookup.
+     */
+    public SecureEventAdminFactory(final EventAdmin admin, final TopicPermissions 
+        topicPermissions)
+    {
+        checkNull(admin, "Admin");
+        checkNull(topicPermissions, "TopicPermissions");
+        
+        m_admin = admin;
+        
+        m_topicPermissions = topicPermissions;
+    }
+
+    /**
+     * Returns a new <tt>EventAdminSecurityDecorator</tt> initialized with the
+     * given <tt>EventAdmin</tt>. That in turn will check any call to post or
+     * send for the appropriate permissions based on the bundle parameter.
+     * 
+     * @param bundle The bundle used to determine the permissions of the caller
+     * @param registration The ServiceRegistration that is not used
+     * 
+     * @return The given service instance wrapped by an <tt>EventAdminSecuriryDecorator</tt> 
+     * 
+     * @see org.osgi.framework.ServiceFactory#getService(org.osgi.framework.Bundle,
+     *      org.osgi.framework.ServiceRegistration)
+     */
+    public Object getService(final Bundle bundle,
+        final ServiceRegistration registration)
+    {
+        // We don't need to cache this objects since the framework already does this.
+        return new EventAdminSecurityDecorator(bundle, m_admin, m_topicPermissions);
+    }
+
+    /**
+     * This method doesn't do anything at the moment.
+     * 
+     * @param bundle The bundle object that is not used
+     * @param registration The ServiceRegistration that is not used
+     * @param service The service object that is not used
+     * 
+     * @see org.osgi.framework.ServiceFactory#ungetService(org.osgi.framework.Bundle,
+     *      org.osgi.framework.ServiceRegistration, java.lang.Object)
+     */
+    public void ungetService(final Bundle bundle,
+        final ServiceRegistration registration, final Object service)
+    {
+        // We don't need to do anything here since we hand-out a new instance with
+        // any call to getService hence, it is o.k. to just wait for the next gc.
+    }
+    
+    /*
+     * This is a utility method that will throw a <tt>NullPointerException</tt>
+     * in case that the given object is null. The message will be of the form name +
+     * may not be null.
+     */
+    private void checkNull(final Object object, final String name)
+    {
+        if(null == object)
+        {
+            throw new NullPointerException(name + " may not be null");
+        }
+    }
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/security/TopicPermissions.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/security/TopicPermissions.java
new file mode 100644
index 0000000..381a7e4
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/security/TopicPermissions.java
@@ -0,0 +1,59 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.security;
+
+/**
+ * A <tt>TopicPermission</tt> factory. The factory is bound to a specific type (i.e.,
+ * either PUBLISH or SUBSCRIBE) and subsequently allows to create new permission
+ * objects by providing the topic. Note that the created permission objects most 
+ * likely will be cached and that in case that a permission can not be created due
+ * to missing security support by the framework (i.e, security is not supported at 
+ * all) an instance of <tt>java.lang.Object</tt> will be returned. 
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public interface TopicPermissions
+{
+    /**
+     * Get the type (i.e., PUBLISH or SUBSCRIBE) of the permission objects that this
+     * factory will create.
+     * 
+     * @return The type of the permission objects that this factory will create.
+     * 
+     * @see org.osgi.service.event.TopicPermission#PUBLISH
+     * @see org.osgi.service.event.TopicPermission#SUBSCRIBE
+     */
+    public String getType();
+    
+    /**
+     * This method returns a <tt>TopicPermission</tt> object for the given topic and
+     * the type (i.e., PUBLISH or SUBSCRIBE) of this factory. Note that this methods
+     * returns an instance of <tt>java.lang.Object</tt> in case that a permission 
+     * could not be created due to missing security support by the framework.
+     *  
+     * @param topic The targeted topic.
+     * 
+     * @return A <tt>TopicPermission</tt> for the given topic and the type of this
+     *      factory or a <tt>java.lang.Object</tt> in case that the permission could 
+     *      not be created due to missing security support by the framework.
+     *
+     * @see org.osgi.service.event.TopicPermission
+     * @see org.osgi.service.event.TopicPermission#PUBLISH
+     * @see org.osgi.service.event.TopicPermission#SUBSCRIBE
+     */
+    public Object createTopicPermission(final String topic);
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/AsyncDeliverTasks.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/AsyncDeliverTasks.java
new file mode 100644
index 0000000..8e8660d
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/AsyncDeliverTasks.java
@@ -0,0 +1,139 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.tasks;
+
+import org.apache.felix.eventadmin.impl.dispatch.TaskQueue;
+import org.apache.felix.eventadmin.impl.dispatch.ThreadPool;
+
+/**
+ * This class does the actual work of the asynchronous event dispatch. 
+ * 
+ * <p>It serves two purposes: first, it will append tasks to its queue hence, 
+ * asynchronous event delivery is executed - second, it will set up a given dispatch 
+ * task with its <tt>ThreadPool</tt> in a way that it is associated with a 
+ * <tt>DeliverTask</tt> that will block in case the thread hits the 
+ * <tt>SyncDeliverTasks</tt>.
+ * </p>
+ * In other words, if the asynchronous event dispatching thread is used to send a 
+ * synchronous event then it will spin-off a new asynchronous dispatching thread
+ * while the former waits for the synchronous event to be delivered and then return
+ * to its <tt>ThreadPool</tt>.
+ *  
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class AsyncDeliverTasks implements DeliverTasks, HandoverTask, DeliverTask
+{
+    // The asynchronous event delivery queue
+    private final TaskQueue m_queue;
+
+    // The synchronous event delivery queue needed in case that the asynchronous 
+    // event dispatching thread is used to send a synchronous event. This is a
+    // private member and only default because it is used in an inner class (for
+    // performance reasons)
+    final TaskQueue m_handoverQueue; 
+    
+    // The thread pool to use to spin-off new threads
+    private final ThreadPool m_pool;
+    
+    /**
+     * The constructor of the class that will use the asynchronous queue to append
+     * event dispatch handlers. Furthermore, a second queue is used to append
+     * the events in case that the asynchronous event dispatching thread is used to
+     * send a synchronous event - in this case the given <tt>ThreadPool</tt> is used
+     * to spin-off a new asynchronous event dispatching thread while the former waits
+     * for the synchronous event to be delivered.
+     * 
+     * @param queue The asynchronous event queue
+     * @param handoverQueue The synchronous event queue, to be used in case that the
+     *      asynchronous event dispatching thread is used to send a synchronous event
+     * @param pool The thread pool used to spin-off new asynchronous event 
+     *      dispatching threads in case of timeout or that the asynchronous event 
+     *      dispatching thread is used to send a synchronous event
+     */
+    public AsyncDeliverTasks(final TaskQueue queue, final TaskQueue handoverQueue, 
+        final ThreadPool pool)
+    {
+        m_queue = queue;
+     
+        m_handoverQueue = handoverQueue;
+        
+        m_pool = pool;
+    }
+    
+    /**
+     * Return a <tt>DeliverTask</tt> that can be used to execute asynchronous event
+     * dispatch.
+     * 
+     * @return A task that can be used to execute asynchronous event dispatch
+     * 
+     * @see org.apache.felix.eventadmin.impl.tasks.DeliverTasks#createTask()
+     */
+    public DeliverTask createTask()
+    {
+        return this;
+    }
+    
+    /**
+     * Execute asynchronous event dispatch.
+     * 
+     * @param tasks The event dispatch tasks to execute
+     * 
+     * @see org.apache.felix.eventadmin.impl.tasks.DeliverTask#execute(org.apache.felix.eventadmin.impl.tasks.HandlerTask[])
+     */
+    public void execute(final HandlerTask[] tasks)
+    {
+        m_queue.append(tasks);
+    }
+    
+    /**
+     * Execute the handover in case of timeout or that the asynchronous event
+     * dispatching thread is used to send a synchronous event.
+     * 
+     * @param task The task to set-up in a new thread
+     *  
+     * @see org.apache.felix.eventadmin.impl.tasks.HandoverTask#execute(org.apache.felix.eventadmin.impl.tasks.DispatchTask)
+     */
+    public void execute(final DispatchTask task)
+    {
+        // This will spin-off a new thread using the thread pool and set it up with
+        // the given task. Additionally, the thread is associated with a callback
+        // that will handover (i.e., yet again call this method) and append the 
+        // tasks given to to the m_handoverQueue (i.e., the synchronous queue). This
+        // will happen in case that the current asynchronous thread is used to 
+        // send a synchronous event. 
+        m_pool.execute(task, new DeliverTask()
+        {
+            public void execute(final HandlerTask[] managers)
+            {
+                final BlockTask waitManager = new BlockTask();
+
+                final HandlerTask[] newmanagers = new HandlerTask[managers.length + 1];
+
+                System.arraycopy(managers, 0, newmanagers, 0,
+                    managers.length);
+
+                newmanagers[managers.length] = waitManager;
+
+                m_handoverQueue.append(newmanagers);
+
+                task.handover();
+
+                waitManager.block();
+            }
+        });
+    }
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/BlockTask.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/BlockTask.java
new file mode 100644
index 0000000..b1896a4
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/BlockTask.java
@@ -0,0 +1,79 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.tasks;
+
+/**
+ * This task will can be used to block a thread that subsequently will be unblocked 
+ * once the task is executed.
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class BlockTask implements HandlerTask
+{
+    // The internal lock for this object used instead synchronized(this)
+    private final Object m_lock = new Object();
+
+    // Has this task not been executed?
+    private boolean m_blocking = true;
+
+    /**
+     * Unblock possibly blocking threads.
+     * 
+     * @see org.apache.felix.eventadmin.impl.tasks.HandlerTask#execute()
+     */
+    public void execute()
+    {
+        synchronized (m_lock)
+        {
+            m_blocking = false;
+            m_lock.notifyAll();
+        }
+    }
+
+    /**
+     * This methods does nothing since we only need this task to block and unblock
+     * threads.
+     * 
+     * @see org.apache.felix.eventadmin.impl.tasks.HandlerTask#blackListHandler()
+     */
+    public void blackListHandler()
+    {
+        // This method does nothing since we only need this task to block and 
+        // unblock threads
+    }
+
+    /**
+     * Block the calling thread until this task is executed.
+     */
+    public void block()
+    {
+        synchronized (m_lock)
+        {
+            while (m_blocking)
+            {
+                try
+                {
+                    m_lock.wait();
+                } catch (InterruptedException e)
+                {
+
+                }
+            }
+        }
+    }
+
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/DeliverTask.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/DeliverTask.java
new file mode 100644
index 0000000..94ed48e
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/DeliverTask.java
@@ -0,0 +1,32 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.tasks;
+
+/**
+ * Dispatch given event dispatch tasks.
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public interface DeliverTask
+{
+    /**
+     * Dispatch the given event dispatch tasks.
+     * 
+     * @param handlerTasks The event dispatch tasks to execute
+     */
+    public void execute(final HandlerTask[] handlerTasks);
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/DeliverTasks.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/DeliverTasks.java
new file mode 100644
index 0000000..7e8f1be
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/DeliverTasks.java
@@ -0,0 +1,33 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.tasks;
+
+/**
+ * A factory that creates <tt>DeliverTask</tt> objects.
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public interface DeliverTasks
+{
+    /**
+     * Create a deliver task.
+     * 
+     * @return A <tt>DeliverTask</tt> that can be used to dispatch event handler 
+     *      tasks
+     */
+    public DeliverTask createTask();
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/DispatchTask.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/DispatchTask.java
new file mode 100644
index 0000000..6473abb
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/DispatchTask.java
@@ -0,0 +1,324 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.tasks;
+
+import org.apache.felix.eventadmin.impl.dispatch.Scheduler;
+import org.apache.felix.eventadmin.impl.dispatch.TaskProducer;
+
+/**
+ * This class is the core of the event dispatching (for both, synchronous and 
+ * asynchronous). It implements handover and timeout capabilities.
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class DispatchTask implements Runnable
+{   
+    // A null scheduler object that does not schedule given tasks
+    private static final Scheduler NULL_SCHEDULER = new Scheduler()
+    {
+        /**
+         * This is a null object and will do nothing with the given task
+         * 
+         * @param task A task that is not used
+         * 
+         * @see org.apache.felix.eventadmin.impl.dispatch.Scheduler#schedule(java.lang.Runnable)
+         */
+        public void schedule(final Runnable task)
+        {
+            // This is a null object and will do nothing with the given task
+        }
+
+        /**
+         * This is a null object and will do nothing with the given task
+         * 
+         * @param task A task that is not used
+         * @parma nice A value that is not used
+         * 
+         * @see org.apache.felix.eventadmin.impl.dispatch.Scheduler#schedule(java.lang.Runnable, int)
+         */
+        public void schedule(final Runnable task, final int nice)
+        {
+            // This is a null object and will do nothing with the given task
+        }
+    };
+
+    // A null producer object that will return null on any call to next()
+    private static final TaskProducer NULL_PRODUCER = new TaskProducer()
+    {
+        /**
+         * This is a null object and will return <tt>null</tt>
+         * 
+         * @return <tt>null</tt>
+         * 
+         * @see org.apache.felix.eventadmin.impl.dispatch.TaskProducer#next()
+         */
+        public HandlerTask next()
+        {
+            return null;
+        }
+    };
+
+    // A null handover task that will do nothing on execute
+    private static final HandoverTask NULL_HANDOVER = new HandoverTask()
+    {
+        /**
+         * This is a null object that will do nothing.
+         * 
+         * @parma task A task that is not used
+         * 
+         * @see org.apache.felix.eventadmin.impl.tasks.HandoverTask#execute(org.apache.felix.eventadmin.impl.tasks.DispatchTask)
+         */
+        public void execute(final DispatchTask task)
+        {
+            // This is a null object that will do nothing.
+        } 
+    };
+    
+    //  The internal lock for this object used instead synchronized(this)
+    final Object m_lock = new Object();
+
+    // The task producer (i.e., the event queue) that will be a null object if not
+    // needed anymore
+    private volatile TaskProducer m_producer;
+
+    // The scheduler to use that will be a null object if not needed anymore
+    private Scheduler m_scheduler;
+
+    // The handover callback that is called on timeouts and handovers and that will
+    // be a null object if not needed anymore
+    private HandoverTask m_handover;
+
+    // Used to blacklist on timeout
+    private BlackListTask m_blackListTask = null;
+
+    // Are we currently blocked (i.e., do not tick the timeout clock down)?
+    private boolean m_isHolding = false;
+
+    /**
+     * The constructor of the object. 
+     * 
+     * @param producer The producer (i.e., the event queue) that provides the next 
+     *      tasks
+     * @param scheduler The scheduler to use for timeout actions
+     * @param handover The callback to use on timeouts and handovers
+     */
+    public DispatchTask(final TaskProducer producer, final Scheduler scheduler,
+        final HandoverTask handover)
+    {
+        m_producer = producer;
+
+        m_scheduler = scheduler;
+
+        m_handover = handover;
+    }
+    
+    /*
+     * Construct a new object from a old one.
+     */
+    private DispatchTask(final DispatchTask old)
+    {
+        this(old.m_producer, old.m_scheduler, old.m_handover);
+    }
+
+    /**
+     * This will loop until the producer returns <tt>null</tt>. Until then the 
+     * returned tasks are executed.
+     * 
+     * @see java.lang.Runnable#run()
+     */
+    public void run()
+    {
+        for (HandlerTask manager = m_producer.next(); null != manager; manager = m_producer
+            .next())
+        {
+            synchronized (m_lock)
+            {
+                // Set-up the timeout 
+                m_blackListTask = new BlackListTask(manager);
+
+                m_scheduler.schedule(m_blackListTask);
+            }
+
+            // HandlerTask does catch exceptions hence, we don't need to do it.
+            manager.execute();
+
+            synchronized (m_lock)
+            {
+                // release the timeout 
+                m_blackListTask.cancel();
+            }
+        }
+    }
+
+    /**
+     * This method will trigger a callback to the handover callback and stop this 
+     * task.
+     */
+    public void handover()
+    {
+        synchronized (m_lock)
+        {
+            // release the timeout
+            m_blackListTask.cancel();
+
+            // spin-off a new thread
+            m_handover.execute(new DispatchTask(this));
+
+            stop();
+        }
+    }
+    
+    /**
+     * This method stops the tasks without a handover
+     */
+    public void stop()
+    {
+        synchronized (m_lock)
+        {
+            // release the timeout
+            m_blackListTask.cancel();
+
+            m_handover = NULL_HANDOVER;
+
+            m_producer = NULL_PRODUCER;
+
+            m_scheduler = NULL_SCHEDULER;
+        }
+    }
+
+    /**
+     * This will pause the task (including its timeout clock) until a call to 
+     * <tt>resume()</tt>
+     */
+    public void hold()
+    {
+        synchronized (m_lock)
+        {
+            // release the timeout
+            m_blackListTask.cancel();
+
+            // record the time that we already used
+            int pastTime = (int) (System.currentTimeMillis() - m_blackListTask
+                .getTime());
+
+            // spin-off a new thread
+            m_handover.execute(new DispatchTask(this));
+
+            // block until a call to resume()
+            m_isHolding = true;
+
+            while (m_isHolding)
+            {
+                try
+                {
+                    m_lock.wait();
+                } catch (InterruptedException e)
+                {
+                }
+            }
+
+            // restore the timeout
+            m_blackListTask = new BlackListTask(m_blackListTask, 
+                System.currentTimeMillis() - pastTime);
+
+            m_scheduler.schedule(m_blackListTask, pastTime);
+        }
+    }
+
+    /**
+     * This will let the previously hold task resume.
+     */
+    public void resume()
+    {
+        synchronized (m_lock)
+        {
+            m_isHolding = false;
+
+            m_lock.notifyAll();
+        }
+    }
+
+    /*
+     * This is the implementation of the timeout.
+     */
+    private class BlackListTask implements Runnable
+    {
+        // Are we canceled?
+        private boolean m_canceled = false;
+
+        // The time we have been started
+        private final long m_time;
+
+        // The task we will blacklist if we are triggered
+        private final HandlerTask m_manager;
+
+        BlackListTask(final HandlerTask manager)
+        {
+            this(manager, System.currentTimeMillis());
+        }
+        
+        BlackListTask(final HandlerTask manager, final long time)
+        {
+            m_manager = manager;
+            
+            m_time = time;
+        }
+
+        BlackListTask(final BlackListTask old, final long time)
+        {
+            this(old.m_manager, time);
+        }
+
+        /**
+         * @return The time we have been created.
+         */
+        public long getTime()
+        {
+            return m_time;
+        }
+
+        /**
+         * We have been triggered hence, blacklist the handler except if we are 
+         * already canceled
+         * 
+         * @see java.lang.Runnable#run()
+         */
+        public void run()
+        {
+            synchronized (m_lock)
+            {
+                if (!m_canceled)
+                {
+                    m_manager.blackListHandler();
+
+                    handover();
+                }
+            }
+        }
+
+        /**
+         * Cancel the timeout
+         */
+        public void cancel()
+        {
+            synchronized (m_lock)
+            {
+                m_canceled = true;
+            }
+        }
+    }
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/HandlerTask.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/HandlerTask.java
new file mode 100644
index 0000000..edf00b6
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/HandlerTask.java
@@ -0,0 +1,36 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.tasks;
+
+/**
+ * A task that will deliver its event to its <tt>EventHandler</tt> when executed
+ * or blacklist the handler, respectively.
+ *  
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public interface HandlerTask
+{
+    /**
+     * Deliver the event to the handler.
+     */
+    public void execute();
+    
+    /**
+     * Blacklist the handler.
+     */
+    public void blackListHandler();
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/HandlerTaskImpl.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/HandlerTaskImpl.java
new file mode 100644
index 0000000..a263762
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/HandlerTaskImpl.java
@@ -0,0 +1,90 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.tasks;
+
+import org.apache.felix.eventadmin.impl.handler.BlacklistingHandlerTasks;
+import org.apache.felix.eventadmin.impl.util.LogWrapper;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventHandler;
+
+/**
+ * An implementation of the <tt>HandlerTask</tt> interface.
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class HandlerTaskImpl implements HandlerTask
+{
+    // The service reference of the handler
+    private final ServiceReference m_eventHandlerRef;
+
+    // The event to deliver to the handler
+    private final Event m_event;
+
+    // Used to blacklist the service or get the service object for the reference
+    private final BlacklistingHandlerTasks m_handlerTasks;
+
+    /**
+     * Construct a delivery task for the given service and event.
+     * 
+     * @param eventHandlerRef The servicereference of the handler
+     * @param event The event to deliver
+     * @param handlerTasks Used to blacklist the service or get the service object
+     *      for the reference 
+     */
+    public HandlerTaskImpl(final ServiceReference eventHandlerRef,
+        final Event event, final BlacklistingHandlerTasks handlerTasks)
+    {
+        m_eventHandlerRef = eventHandlerRef;
+
+        m_event = event;
+
+        m_handlerTasks = handlerTasks;
+    }
+
+    /**
+     * @see org.apache.felix.eventadmin.impl.tasks.HandlerTask#execute()
+     */
+    public void execute()
+    {
+        // Get the service object 
+        final EventHandler handler = m_handlerTasks
+            .getEventHandler(m_eventHandlerRef);
+
+        try
+        {
+            handler.handleEvent(m_event);
+        } catch (Exception e)
+        {
+            // The spec says that we must catch exceptions and log them:
+            LogWrapper.getLogger().log(
+                m_eventHandlerRef,
+                LogWrapper.LOG_WARNING,
+                "Exception during event dispatch [" + m_event + " | "
+                    + m_eventHandlerRef + " | Bundle("
+                    + m_eventHandlerRef.getBundle() + ")]", e);
+        }
+    }
+
+    /**
+     * @see org.apache.felix.eventadmin.impl.tasks.HandlerTask#blackListHandler()
+     */
+    public void blackListHandler()
+    {
+        m_handlerTasks.blackList(m_eventHandlerRef);
+    }
+}
\ No newline at end of file
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/HandoverTask.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/HandoverTask.java
new file mode 100644
index 0000000..5ae14ce
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/HandoverTask.java
@@ -0,0 +1,32 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.tasks;
+
+/**
+ * A task that is used to handover a dispatch thread context to another thread.
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public interface HandoverTask
+{
+    /**
+     * Handover the context to another thread.
+     * 
+     * @param task The context to be executed in another thread.
+     */
+    public void execute(final DispatchTask task);
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/ResumeTask.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/ResumeTask.java
new file mode 100644
index 0000000..82ab8f41
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/ResumeTask.java
@@ -0,0 +1,66 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.tasks;
+
+import org.apache.felix.eventadmin.impl.dispatch.ThreadPool;
+
+/**
+ * A task that wakes-up a disabled <tt>DispatchTask</tt>. Additionally, it will
+ * stop the currently running task.
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class ResumeTask implements HandlerTask
+{
+    // The task to wake-up on execution
+    private final DispatchTask m_target;
+    
+    // The pool used to get the task to stop on execution
+    private final ThreadPool m_pool;
+    
+    /**
+     * @param target The task to wake-up on execution
+     * @param pool The pool used to get the task to stop on execution
+     */
+    public ResumeTask(final DispatchTask target, final ThreadPool pool)
+    {
+        m_target = target;
+        
+        m_pool = pool;
+    }
+    
+    /**
+     * Stop the current task and wake-up the target.
+     * 
+     * @see org.apache.felix.eventadmin.impl.tasks.HandlerTask#execute()
+     */
+    public void execute()
+    {
+        m_pool.getTask(Thread.currentThread(), null).stop();
+        
+        m_target.resume();
+    }
+
+    /**
+     * This does nothing since this task is only used to wake-up disabled tasks.
+     * 
+     * @see org.apache.felix.eventadmin.impl.tasks.HandlerTask#blackListHandler()
+     */
+    public void blackListHandler()
+    {
+    }
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/SyncDeliverTasks.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/SyncDeliverTasks.java
new file mode 100644
index 0000000..3854436
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/tasks/SyncDeliverTasks.java
@@ -0,0 +1,139 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.tasks;
+
+import org.apache.felix.eventadmin.impl.dispatch.TaskQueue;
+import org.apache.felix.eventadmin.impl.dispatch.ThreadPool;
+
+/**
+ * This class does the actual work of the synchronous event delivery.
+ * <p><tt>
+ * It serves two purposes, first it is used to select the appropriate action
+ * depending on whether the sending thread is the asynchronous, the synchronous, or
+ * an unrelated thread. Second, it will set up a given dispatch 
+ * task with its <tt>ThreadPool</tt> in a way that it is associated with a 
+ * <tt>DeliverTask</tt> that will push given handler tasks to the queue and
+ * then wait for the tasks to be completed.
+ * </tt></p>
+ * In other words if an unrelated thread is used to send a synchronous event it is
+ * blocked until the event is send (or a timeout occurs), if an asynchronous thread
+ * is used its handover callback is called in order to spin-off a new asynchronous
+ * delivery thread and the former is blocked until the events are delivered and then 
+ * released (or returned to its thread pool), if a synchronous thread is used its 
+ * task is disabled, the events are pushed to the queue and the threads continuous 
+ * with the delivery of the new events (as per spec). Once the new events are done
+ * the thread wakes-up the disabled task and resumes to execute it. 
+ * <p><tt>
+ * Note that in case of a timeout while a task is disabled the thread is released and
+ * we spin-off a new thread that resumes the disabled task hence, this is the only
+ * place were we break the semantics of the synchronous delivery. While the only one
+ * to notice this is the timed-out handler - it is the fault of this handler too 
+ * (i.e., it blocked the dispatch for to long) but since it will not receive events 
+ * anymore it will not notice this semantic difference except that it might not see 
+ * events it already sent before.
+ * </tt></pre>
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class SyncDeliverTasks implements DeliverTasks, HandoverTask, DeliverTask
+{
+    // The synchronous event queue
+    final TaskQueue m_queue;
+
+    // The thread pool used to spin-off new threads and associate callbacks with
+    // tasks
+    final ThreadPool m_pool;
+
+    /**
+     * @param queue The synchronous event queue
+     * @param pool The thread pool used to spin-off new threads and associate 
+     *      callbacks with tasks
+     */
+    public SyncDeliverTasks(final TaskQueue queue, final ThreadPool pool)
+    {
+        m_queue = queue;
+
+        m_pool = pool;
+    }
+
+    /**
+     * This will select the appropriate action depending on whether the sending
+     * thread is the asynchronous, the synchronous, or an unrelated thread.
+     * 
+     * @return The appropriate action
+     * 
+     * @see org.apache.felix.eventadmin.impl.tasks.DeliverTasks#createTask()
+     */
+    public DeliverTask createTask()
+    {
+        return m_pool.getCallback(Thread.currentThread(), this);
+    }
+    
+    /**
+     * This blocks an unrelated thread used to send a synchronous event until the 
+     * event is send (or a timeout occurs).
+     * 
+     * @param tasks The event handler dispatch tasks to execute
+     * 
+     * @see org.apache.felix.eventadmin.impl.tasks.DeliverTask#execute(org.apache.felix.eventadmin.impl.tasks.HandlerTask[])
+     */
+    public void execute(final HandlerTask[] tasks)
+    {
+        final BlockTask waitManager = new BlockTask();
+
+        final HandlerTask[] newtasks = new HandlerTask[tasks.length + 1];
+        
+        System.arraycopy(tasks, 0, newtasks, 0, tasks.length);
+        
+        newtasks[tasks.length] = waitManager;
+
+        m_queue.append(newtasks);
+
+        waitManager.block();
+    }
+    
+    /**
+     * Set up a given dispatch task with its <tt>ThreadPool</tt> in a way that it is 
+     * associated with a <tt>DeliverTask</tt> that will push given handler tasks to 
+     * the queue and then wait for the tasks to be completed.
+     * 
+     * @param task The task to set-up
+     * 
+     * @see org.apache.felix.eventadmin.impl.tasks.HandoverTask#execute(org.apache.felix.eventadmin.impl.tasks.DispatchTask)
+     */
+    public void execute(final DispatchTask task)
+    {
+        m_pool.execute(task, new DeliverTask()
+        {
+            public void execute(final HandlerTask[] managers)
+            {
+                final ResumeTask resumeManager = new ResumeTask(
+                    task, m_pool);
+
+                final HandlerTask[] newmanagers = new HandlerTask[managers.length + 1];
+
+                System.arraycopy(managers, 0, newmanagers, 0,
+                    managers.length);
+
+                newmanagers[managers.length] = resumeManager;
+
+                m_queue.push(newmanagers);
+
+                task.hold();
+            }
+        });
+    }
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/util/CacheMap.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/util/CacheMap.java
new file mode 100644
index 0000000..93e007f
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/util/CacheMap.java
@@ -0,0 +1,65 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.util;
+
+/**
+ * This is the interface of a simple cache map. 
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public interface CacheMap
+{
+    /**
+     * Return the value for the key in case there is one in the cache.
+     * 
+     * @param key The key to look-up
+     * 
+     * @return The value for the given key in case there is one in the cache, 
+     *      <tt>null</tt> otherwise
+     */
+    public Object get(final Object key);
+    
+    /**
+     * Add a value for the key to this cache.
+     * 
+     * @param key The key for the value
+     * @param value The value to add to the cache 
+     */
+    public void add(final Object key, final Object value);
+    
+    /**
+     * Remove a key and its value from the cache.
+     * 
+     * @param key The key to remove
+     * 
+     * @return The value of the key in case there is one in the cache, <tt>null</tt>
+     *      otherwise
+     */
+    public Object remove(final Object key);
+    
+    /**
+     * Returns the number of key-value pairs in this cache.
+     * 
+     * @return The number of key-value pairs in this cache
+     */
+    public int size();
+    
+    /**
+     * Remove all entries of the cache.
+     */
+    public void clear();
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/util/LeastRecentlyUsedCacheMap.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/util/LeastRecentlyUsedCacheMap.java
new file mode 100644
index 0000000..6fd0901
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/util/LeastRecentlyUsedCacheMap.java
@@ -0,0 +1,186 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.util;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class implements a least recently used cache map. It will hold 
+ * a given size of key-value pairs and drop the least recently used entry once this
+ * size is reached. This class is thread safe.
+ * 
+ * @see org.apache.felix.eventadmin.impl.util.CacheMap
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class LeastRecentlyUsedCacheMap implements CacheMap
+{
+    // The internal lock for this object used instead synchronized(this)
+    private final Object m_lock = new Object();
+    
+    // The max number of entries in the cache. Once reached entries are replaced
+    private final int m_maxSize;
+    
+    // The cache 
+    private final Map m_cache;
+    
+    // The history used to determine the least recently used entries. The end of the
+    // list is the most recently used key. In other words m_history.get(0) returns
+    // the least recently used key.
+    private final List m_history;
+    
+    /**
+     * The constructor of the cache. The given max size will be used to determine the
+     * size of the cache that triggers replacing least recently used entries with 
+     * new ones.
+     * 
+     * @param maxSize The max number of entries in the cache
+     */
+    public LeastRecentlyUsedCacheMap(final int maxSize)
+    {
+        if(0 >= maxSize)
+        {
+            throw new IllegalArgumentException("Size must be positive");
+        }
+        
+        m_maxSize = maxSize;
+        
+        // We need one more entry then m_maxSize in the cache and a HashMap is
+        // expanded when it reaches 3/4 of its size hence, the funny numbers.
+        m_cache = new HashMap(m_maxSize + 1 + ((m_maxSize + 1) * 3) / 4);
+        
+        // This is like above but assumes a list is expanded when it reaches 1/2 of
+        // its size. Not much harm if this is not the case. 
+        m_history  = new ArrayList(m_maxSize + 1 + ((m_maxSize + 1) / 2));
+    }
+    
+    /**
+     * Returns the value for the key in case there is one. Additionally, the 
+     * LRU counter for the key is updated.
+     * 
+     * @param key The key for the value to return
+     * 
+     * @return The value of the key in case there is one, <tt>null</tt> otherwise
+     * 
+     * @see org.apache.felix.eventadmin.impl.util.CacheMap#get(java.lang.Object)
+     */
+    public Object get(final Object key)
+    {
+        synchronized(m_lock)
+        {
+            final Object result = m_cache.get(key);
+            
+            if(null != result)
+            {
+                m_history.remove(key);
+                
+                m_history.add(key);
+            }
+            
+            return result;
+        }
+    }
+
+    /**
+     * Add the key-value pair to the cache. The key will be come the most recently
+     * used entry. In case max size is (or has been) reached this will remove the
+     * least recently used entry in the cache. In case that the cache already 
+     * contains this specific key-value pair it LRU counter is updated only.
+     * 
+     * @param key The key for the value
+     * @param value The value for the key
+     * 
+     * @see org.apache.felix.eventadmin.impl.util.CacheMap#add(java.lang.Object, java.lang.Object)
+     */
+    public void add(final Object key, final Object value)
+    {
+        synchronized(m_lock)
+        {
+            final Object result = m_cache.put(key, value);
+            
+            if(null != result)
+            {
+                m_history.remove(key);
+            }
+            
+            m_history.add(key);
+            
+            if(m_maxSize < m_cache.size())
+            {
+                m_cache.remove(m_history.remove(0));
+            }
+        }
+    }
+
+    /**
+     * Remove the entry denoted by key from the cache and return its value.
+     * 
+     * @param key The key of the entry to be removed
+     * 
+     * @return The value of the entry removed, <tt>null</tt> if none
+     * 
+     * @see org.apache.felix.eventadmin.impl.util.CacheMap#remove(java.lang.Object)
+     */
+    public Object remove(final Object key)
+    {
+        synchronized(m_lock)
+        {
+            final Object result = m_cache.remove(key);
+            
+            if(null != result)
+            {
+                m_history.remove(key);
+            }
+            
+            return result;
+        }
+    }
+
+    /**
+     * Return the current size of the cache.
+     * 
+     * @return The number of entries currently in the cache.
+     * 
+     * @see org.apache.felix.eventadmin.impl.util.CacheMap#size()
+     */
+    public int size()
+    {
+        synchronized (m_lock)
+        {
+            return m_cache.size();
+        }
+    }
+
+    /**
+     * Remove all entries from the cache.
+     * 
+     * @see org.apache.felix.eventadmin.impl.util.CacheMap#clear()
+     */
+    public void clear()
+    {
+        synchronized (m_lock)
+        {
+            m_cache.clear();
+            
+            m_history.clear();
+        }
+    }
+
+}
diff --git a/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/util/LogWrapper.java b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/util/LogWrapper.java
new file mode 100644
index 0000000..e1cc5e7
--- /dev/null
+++ b/org.apache.felix.eventadmin/src/main/java/org/apache/felix/eventadmin/impl/util/LogWrapper.java
@@ -0,0 +1,400 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.eventadmin.impl.util;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * This class mimics the standard OSGi <tt>LogService</tt> interface. An
+ * instance of this class will be used by the EventAdmin for all logging. The 
+ * implementation of this class sends log messages to standard output, if no
+ * <tt>LogService</tt> is present; it uses a log service if one is
+ * installed in the framework. To do that without creating a hard dependency on the 
+ * package it uses fully qualified class names and registers a listener with the
+ * framework hence, it does not need access to the <tt>LogService</tt> class but will 
+ * use it if the listener is informed about an available service. By using a 
+ * DynamicImport-Package dependency we don't need the package but
+ * use it if present. Additionally, all log methods prefix the log message with
+ * <tt>EventAdmin: </tt>.
+ * 
+ * @see org.osgi.service.log.LogService
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+**/
+// TODO: At the moment we log a message to all currently available LogServices. 
+//       Maybe, we should only log to the one with the highest ranking instead?
+//       What is the best practice in this case? 
+public class LogWrapper
+{
+    /**
+     * ERROR LEVEL
+     * 
+     * @see org.osgi.service.log.LogService#LOG_ERROR 
+     */
+    public static final int LOG_ERROR = 1;
+
+    /**
+     * WARNING LEVEL
+     * 
+     * @see org.osgi.service.log.LogService#LOG_WARNING
+     */
+    public static final int LOG_WARNING = 2;
+
+    /**
+     * INFO LEVEL
+     * 
+     * @see org.osgi.service.log.LogService#LOG_INFO
+     */
+    public static final int LOG_INFO = 3;
+
+    /**
+     * DEBUG LEVEL
+     * 
+     * @see org.osgi.service.log.LogService#LOG_DEBUG
+     */
+    public static final int LOG_DEBUG = 4;
+
+    // A set containing the currently available LogServices. Furthermore used as lock
+    private final Set m_loggerRefs = new HashSet();
+
+    // Only null while not set and m_loggerRefs is empty hence, only needs to be 
+    // checked in case m_loggerRefs is empty otherwise it will not be null.
+    private BundleContext m_context;
+
+    /*
+     * A thread save variant of the double checked locking singleton.
+     */
+    private static class LogWrapperLoader
+    {
+        static final LogWrapper m_singleton = new LogWrapper();
+    }
+    
+    /**
+     * Returns the singleton instance of this LogWrapper that can be used to send
+     * log messages to all currently available LogServices or to standard output,
+     * respectively.
+     * 
+     * @return the singleton instance of this LogWrapper.
+     */
+    public static LogWrapper getLogger()
+    {
+        return LogWrapperLoader.m_singleton;
+    }
+
+    /**
+     * Set the <tt>BundleContext</tt> of the bundle. This method registers a service
+     * listener for LogServices with the framework that are subsequently used to 
+     * log messages.
+     * 
+     *  @param context The context of the bundle.
+     */
+    public static void setContext(final BundleContext context)
+    {
+        LogWrapperLoader.m_singleton.setBundleContext(context);
+
+        try
+        {
+            context.addServiceListener(new ServiceListener()
+            {
+                // Add a newly available LogService reference to the singleton.
+                public void serviceChanged(final ServiceEvent event)
+                {
+                    if (ServiceEvent.REGISTERED == event.getType())
+                    {
+                        LogWrapperLoader.m_singleton.addLoggerRef(
+                            event.getServiceReference());
+                    }
+                    // unregistered services are handled in the next log operation.
+                }
+
+            }, "(" + Constants.OBJECTCLASS
+                + "=org.osgi.service.log.LogService)");
+
+            // Add all available LogService references to the singleton.
+            final ServiceReference[] refs = context.getServiceReferences(
+                "org.osgi.service.log.LogService", null);
+
+            if (null != refs)
+            {
+                for (int i = 0; i < refs.length; i++)
+                {
+                    LogWrapperLoader.m_singleton.addLoggerRef(refs[i]);
+                }
+            }
+        } catch (InvalidSyntaxException e)
+        {
+            // this never happens
+        }
+    } 
+
+    /*
+     * The private singleton constructor.
+     */
+    LogWrapper()
+    {
+        // Singleton
+    }
+
+    /*
+     * Add a reference to a newly available LogService
+     */
+    void addLoggerRef(final ServiceReference ref)
+    {
+        synchronized (m_loggerRefs)
+        {
+            m_loggerRefs.add(ref);
+        }
+    }
+
+    /*
+     * Set the context of the bundle in the singleton implementation.
+     */
+    private void setBundleContext(final BundleContext context)
+    {
+        synchronized(m_loggerRefs)
+        {
+            m_context = context;
+        }
+    }
+
+    /**
+     * Log a message with the given log level. Note that this will prefix the message
+     * with <tt>EventAdmin: </tt>.
+     * 
+     * @param level The log level with which to log the msg.
+     * @param msg The message to log.
+     */
+    public void log(final int level, final String msg)
+    {
+        // The method will remove any unregistered service reference as well.
+        synchronized(m_loggerRefs)
+        {
+            final String logMsg = "EventAdmin: " + msg;
+            
+            if (!m_loggerRefs.isEmpty())
+            {
+                // There is at least one LogService available hence, we can use the
+                // class as well.
+                for (Iterator iter = m_loggerRefs.iterator(); iter.hasNext();)
+                {
+                    org.osgi.service.log.LogService logger = 
+                        (org.osgi.service.log.LogService) m_context.getService(
+                        (ServiceReference) iter.next());
+    
+                    if (null != logger)
+                    {
+                        logger.log(level, logMsg);
+                    }
+                    else
+                    {
+                        // The context returned null for the reference - it follows 
+                        // that the service is unregistered and we can remove it
+                        iter.remove();
+                    }
+                }
+            }
+            else
+            {
+                _log(null, level, logMsg, null);
+            }
+        }
+    }
+
+    /**
+     * Log a message with the given log level and the associated exception. Note that 
+     * this will prefix the message with <tt>EventAdmin: </tt>.
+     * 
+     * @param level The log level with which to log the msg.
+     * @param msg The message to log.
+     * @param ex The exception associated with the message.
+     */
+    public void log(final int level, final String msg, final Throwable ex)
+    {
+        // The method will remove any unregistered service reference as well.
+        synchronized(m_loggerRefs)
+        {
+            final String logMsg = "EventAdmin: " + msg;
+            
+            if (!m_loggerRefs.isEmpty())
+            {
+                // There is at least one LogService available hence, we can use the
+                // class as well.
+                for (Iterator iter = m_loggerRefs.iterator(); iter.hasNext();)
+                {
+                    org.osgi.service.log.LogService logger = 
+                        (org.osgi.service.log.LogService) m_context.getService(
+                        (ServiceReference) iter.next());
+    
+                    if (null != logger)
+                    {
+                        logger.log(level, logMsg, ex);
+                    }
+                    else
+                    {
+                        // The context returned null for the reference - it follows 
+                        // that the service is unregistered and we can remove it
+                        iter.remove();
+                    }
+                }
+            }
+            else
+            {
+                _log(null, level, logMsg, ex);
+            }
+        }
+    }
+
+    /**
+     * Log a message with the given log level together with the associated service
+     * reference. Note that this will prefix the message with <tt>EventAdmin: </tt>.
+     * 
+     * @param sr The reference of the service associated with this message.
+     * @param level The log level with which to log the msg.
+     * @param msg The message to log.
+     */
+    public void log(final ServiceReference sr, final int level, final String msg)
+    {
+        // The method will remove any unregistered service reference as well.
+        synchronized(m_loggerRefs)
+        {
+            final String logMsg = "EventAdmin: " + msg;
+            
+            if (!m_loggerRefs.isEmpty())
+            {
+                // There is at least one LogService available hence, we can use the
+                // class as well.
+                for (Iterator iter = m_loggerRefs.iterator(); iter.hasNext();)
+                {
+                    org.osgi.service.log.LogService logger = 
+                        (org.osgi.service.log.LogService) m_context.getService(
+                        (ServiceReference) iter.next());
+    
+                    if (null != logger)
+                    {
+                        logger.log(sr, level, logMsg);
+                    }
+                    else
+                    {
+                        // The context returned null for the reference - it follows 
+                        // that the service is unregistered and we can remove it
+                        iter.remove();
+                    }
+                }
+            }
+            else
+            {
+                _log(sr, level, logMsg, null);
+            }
+        }
+    }
+
+    /**
+     * Log a message with the given log level, the associated service reference and
+     * exception. Note that this will prefix the message with <tt>EventAdmin: </tt>.
+     * 
+     * @param sr The reference of the service associated with this message.
+     * @param level The log level with which to log the msg.
+     * @param msg The message to log.
+     * @param ex The exception associated with the message.
+     */
+    public void log(final ServiceReference sr, final int level, final String msg, 
+        final Throwable ex)
+    {
+        // The method will remove any unregistered service reference as well.
+        synchronized(m_loggerRefs)
+        {
+            final String logMsg = "EventAdmin: " + msg;
+            
+            if (!m_loggerRefs.isEmpty())
+            {
+                // There is at least one LogService available hence, we can use the
+                // class as well.
+                for (Iterator iter = m_loggerRefs.iterator(); iter.hasNext();)
+                {
+                    org.osgi.service.log.LogService logger = 
+                        (org.osgi.service.log.LogService) m_context.getService(
+                        (ServiceReference) iter.next());
+    
+                    if (null != logger)
+                    {
+                        logger.log(sr, level, logMsg, ex);
+                    }
+                    else
+                    {
+                        // The context returned null for the reference - it follows 
+                        // that the service is unregistered and we can remove it
+                        iter.remove();
+                    }
+                }
+            }
+            else
+            {
+                _log(sr, level, logMsg, ex);
+            }
+        }
+    }
+
+    /*
+     * Log the message to standard output. This appends the level to the message.
+     * null values are handled appropriate. 
+     */
+    private void _log(final ServiceReference sr, final int level, final String msg, 
+        Throwable ex)
+    {
+        String s = (sr == null) ? null : "SvcRef " + sr;
+        s = (s == null) ? msg : s + " " + msg;
+        s = (ex == null) ? s : s + " (" + ex + ")";
+        
+        switch (level)
+        {
+            case LOG_DEBUG:
+                System.out.println("DEBUG: " + s);
+                break;
+            case LOG_ERROR:
+                System.out.println("ERROR: " + s);
+                if (ex != null)
+                {
+                    if ((ex instanceof BundleException)
+                        && (((BundleException) ex).getNestedException() != null))
+                    {
+                        ex = ((BundleException) ex).getNestedException();
+                    }
+                    
+                    ex.printStackTrace();
+                }
+                break;
+            case LOG_INFO:
+                System.out.println("INFO: " + s);
+                break;
+            case LOG_WARNING:
+                System.out.println("WARNING: " + s);
+                break;
+            default:
+                System.out.println("UNKNOWN[" + level + "]: " + s);
+        }
+    }
+}