You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gn...@apache.org on 2014/03/18 08:08:13 UTC

git commit: [KARAF-2813] Use EventAdmin to provide an audit trail of events, but it's lacking the authenticated Subject who performed the action

Repository: karaf
Updated Branches:
  refs/heads/master 9ba0b6580 -> 2fe4f77be


[KARAF-2813] Use EventAdmin to provide an audit trail of events, but it's lacking the authenticated Subject who performed the action


Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/2fe4f77b
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/2fe4f77b
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/2fe4f77b

Branch: refs/heads/master
Commit: 2fe4f77bea54296ba04493b43399a6941088aa32
Parents: 9ba0b65
Author: Guillaume Nodet <gn...@gmail.com>
Authored: Tue Mar 18 08:06:28 2014 +0100
Committer: Guillaume Nodet <gn...@gmail.com>
Committed: Tue Mar 18 08:06:40 2014 +0100

----------------------------------------------------------------------
 assemblies/features/standard/pom.xml            |   4 +-
 .../standard/src/main/feature/feature.xml       |   6 +-
 .../karaf/features/internal/BundleManager.java  |   2 +-
 pom.xml                                         |   7 +
 services/eventadmin/pom.xml                     | 113 ++++
 .../felix/eventadmin/impl/Configuration.java    | 516 +++++++++++++++++++
 .../eventadmin/impl/handler/EventAdminImpl.java | 197 +++++++
 services/pom.xml                                |  41 ++
 8 files changed, 882 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/2fe4f77b/assemblies/features/standard/pom.xml
----------------------------------------------------------------------
diff --git a/assemblies/features/standard/pom.xml b/assemblies/features/standard/pom.xml
index f7dd80a..9f2b96a 100644
--- a/assemblies/features/standard/pom.xml
+++ b/assemblies/features/standard/pom.xml
@@ -465,8 +465,8 @@
 
         <!-- eventadmin deps -->
         <dependency>
-            <groupId>org.apache.felix</groupId>
-            <artifactId>org.apache.felix.eventadmin</artifactId>
+            <groupId>org.apache.karaf.services</groupId>
+            <artifactId>org.apache.karaf.services.eventadmin</artifactId>
             <scope>provided</scope>
         </dependency>
 

http://git-wip-us.apache.org/repos/asf/karaf/blob/2fe4f77b/assemblies/features/standard/src/main/feature/feature.xml
----------------------------------------------------------------------
diff --git a/assemblies/features/standard/src/main/feature/feature.xml b/assemblies/features/standard/src/main/feature/feature.xml
index 1143970..243ab52 100644
--- a/assemblies/features/standard/src/main/feature/feature.xml
+++ b/assemblies/features/standard/src/main/feature/feature.xml
@@ -171,7 +171,11 @@
     </feature>
 
     <feature name="eventadmin" description="OSGi Event Admin service specification for event-based communication" version="${project.version}" resolver="(obr)">
-        <bundle start-level="30">mvn:org.apache.felix/org.apache.felix.eventadmin/${felix.eventadmin.version}</bundle>
+        <config name="org.apache.felix.eventadmin.impl.EventAdmin">
+            org.apache.felix.eventadmin.AddTimestamp=true
+            org.apache.felix.eventadmin.AddSubject=true
+        </config>
+        <bundle start-level="30">mvn:org.apache.karaf.services/org.apache.karaf.services.eventadmin/${project.version}</bundle>
         <conditional>
             <condition>webconsole</condition>
             <bundle start-level="30">mvn:org.apache.felix/org.apache.felix.webconsole.plugins.event/${felix.eventadmin.webconsole.plugin.version}</bundle>

http://git-wip-us.apache.org/repos/asf/karaf/blob/2fe4f77b/features/core/src/main/java/org/apache/karaf/features/internal/BundleManager.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/BundleManager.java b/features/core/src/main/java/org/apache/karaf/features/internal/BundleManager.java
index cd744f0..04a5f5b 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/BundleManager.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/BundleManager.java
@@ -428,7 +428,7 @@ public class BundleManager {
         EventAdminListener listener = null;
         try {
             getClass().getClassLoader().loadClass(
-                    "org.bundles.service.event.EventAdmin");
+                    "org.osgi.service.event.EventAdmin");
             listener = new EventAdminListener(bundleContext);
         } catch (Throwable t) {
             // Ignore, if the EventAdmin package is not available, just don't

http://git-wip-us.apache.org/repos/asf/karaf/blob/2fe4f77b/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index ae4da3a..68b6029 100644
--- a/pom.xml
+++ b/pom.xml
@@ -70,6 +70,7 @@
         <module>demos</module>
         <module>archetypes</module>
         <module>itests</module>
+        <module>services</module>
     </modules>
 
     <scm>
@@ -724,6 +725,12 @@
             </dependency>
 
             <dependency>
+                <groupId>org.apache.karaf.services</groupId>
+                <artifactId>org.apache.karaf.services.eventadmin</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+
+            <dependency>
                 <groupId>org.apache.karaf.scr</groupId>
                 <artifactId>org.apache.karaf.scr.command</artifactId>
                 <version>${project.version}</version>

http://git-wip-us.apache.org/repos/asf/karaf/blob/2fe4f77b/services/eventadmin/pom.xml
----------------------------------------------------------------------
diff --git a/services/eventadmin/pom.xml b/services/eventadmin/pom.xml
new file mode 100644
index 0000000..3cf1325
--- /dev/null
+++ b/services/eventadmin/pom.xml
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <!--
+
+        Licensed to the Apache Software Foundation (ASF) under one or more
+        contributor license agreements.  See the NOTICE file distributed with
+        this work for additional information regarding copyright ownership.
+        The ASF licenses this file to You under the Apache License, Version 2.0
+        (the "License"); you may not use this file except in compliance with
+        the License.  You may obtain a copy of the License at
+
+           http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <artifactId>karaf</artifactId>
+        <groupId>org.apache.karaf</groupId>
+        <version>3.1.0-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <groupId>org.apache.karaf.services</groupId>
+    <artifactId>org.apache.karaf.services.eventadmin</artifactId>
+    <packaging>bundle</packaging>
+    <name>Apache Karaf :: OSGi Services :: EventAdmin</name>
+    <description>EventAdmin Service</description>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Bundle-SymbolicName>
+                            ${pom.artifactId}
+                        </Bundle-SymbolicName>
+                        <Bundle-Activator>
+                            org.apache.felix.eventadmin.impl.Activator
+                        </Bundle-Activator>
+                        <Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
+                        <DynamicImport-Package>
+                            org.osgi.service.log;version="[1.3,2)"
+                        </DynamicImport-Package>
+                        <Import-Package>
+                            <!--
+                                Configuration Admin is optional and if it is
+                                present, version 1.2 (from R4.0) is enough
+                            -->
+                            org.osgi.service.cm;version="[1.2,2)";resolution:=optional,
+
+                            <!--
+                                Metatype is optional and if it is
+                                present, version 1.1 (from R4.1) is enough
+                            -->
+                            org.osgi.service.metatype;version="[1.1,2)";resolution:=optional,
+
+                            <!--
+                                Optional import to back the dynamic import on org.osgi.service.log
+                            -->
+                            org.osgi.service.log;version="[1.3,2)";resolution:=optional,
+
+                            <!-- default -->
+                            *
+                        </Import-Package>
+                        <Export-Package>org.osgi.service.event</Export-Package>
+                        <Private-Package>
+                            org.apache.felix.eventadmin.impl.*;-split-package:=merge-first,
+                            EDU.oswego.cs.dl.util.concurrent,
+                            org.osgi.util.tracker
+                        </Private-Package>
+                        <Import-Service>
+                            org.osgi.service.event.EventHandler;availability:=optional;multiple:=true,
+                            org.osgi.service.log.LogService;availability:=optional;multiple:=false,
+                            org.osgi.service.log.LogReaderService;availability:=optional;multiple:=false
+                        </Import-Service>
+                        <Export-Service>
+                            org.osgi.service.event.EventAdmin
+                        </Export-Service>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.eventadmin</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/karaf/blob/2fe4f77b/services/eventadmin/src/main/java/org/apache/felix/eventadmin/impl/Configuration.java
----------------------------------------------------------------------
diff --git a/services/eventadmin/src/main/java/org/apache/felix/eventadmin/impl/Configuration.java b/services/eventadmin/src/main/java/org/apache/felix/eventadmin/impl/Configuration.java
new file mode 100644
index 0000000..ccc722d
--- /dev/null
+++ b/services/eventadmin/src/main/java/org/apache/felix/eventadmin/impl/Configuration.java
@@ -0,0 +1,516 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.eventadmin.impl;
+
+
+import java.util.*;
+
+import org.apache.felix.eventadmin.impl.adapter.*;
+import org.apache.felix.eventadmin.impl.handler.EventAdminImpl;
+import org.apache.felix.eventadmin.impl.security.SecureEventAdminFactory;
+import org.apache.felix.eventadmin.impl.tasks.DefaultThreadPool;
+import org.apache.felix.eventadmin.impl.util.LogWrapper;
+import org.osgi.framework.*;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.service.metatype.MetaTypeProvider;
+
+
+/**
+ * The <code>Configuration</code> class encapsules the
+ * configuration for the event admin.
+ *
+ * The service knows about the following properties which are read at bundle startup:
+ * <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>
+ * <p>
+ * <p>
+ *      <tt>org.apache.felix.eventadmin.IgnoreTimeout</tt> - Configure
+ *         <tt>EventHandler</tt>s to be called without a timeout.
+ * </p>
+ * If a timeout is configured by default all event handlers are called using the timeout.
+ * For performance optimization it is possible to configure event handlers where the
+ * timeout handling is not used - this reduces the thread usage from the thread pools
+ * as the timout handling requires an additional thread to call the event handler.
+ * However, the application should work without this configuration property. It is a
+ * pure optimization!
+ * The value is a list of string (separated by comma). If the string ends with a dot,
+ * all handlers in exactly this package are ignored. If the string ends with a star,
+ * all handlers in this package and all subpackages are ignored. If the string neither
+ * ends with a dot nor with a start, this is assumed to define an exact class name.
+ *
+ * These properties are read at startup and serve as a default configuration.
+ * If a configuration admin is configured, the event admin can be configured
+ * through the config admin.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Configuration
+{
+    /** The PID for the event admin. */
+    static final String PID = "org.apache.felix.eventadmin.impl.EventAdmin";
+
+    static final String PROP_THREAD_POOL_SIZE = "org.apache.felix.eventadmin.ThreadPoolSize";
+    static final String PROP_TIMEOUT = "org.apache.felix.eventadmin.Timeout";
+    static final String PROP_REQUIRE_TOPIC = "org.apache.felix.eventadmin.RequireTopic";
+    static final String PROP_IGNORE_TIMEOUT = "org.apache.felix.eventadmin.IgnoreTimeout";
+    static final String PROP_LOG_LEVEL = "org.apache.felix.eventadmin.LogLevel";
+    static final String PROP_ADD_TIMESTAMP = "org.apache.felix.eventadmin.AddTimestamp";
+    static final String PROP_ADD_SUBJECT = "org.apache.felix.eventadmin.AddSubject";
+
+    /** The bundle context. */
+    private final BundleContext m_bundleContext;
+
+    private int m_threadPoolSize;
+
+    private int m_timeout;
+
+    private boolean m_requireTopic;
+
+    private String[] m_ignoreTimeout;
+
+    private int m_logLevel;
+
+    private boolean m_addTimestamp;
+
+    private boolean m_addSubject;
+
+    // The thread pool used - this is a member because we need to close it on stop
+    private volatile DefaultThreadPool m_sync_pool;
+
+    private volatile DefaultThreadPool m_async_pool;
+
+    // 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;
+
+    // The registration of the security decorator factory (i.e., the service)
+    private volatile ServiceRegistration m_registration;
+
+    // all adapters
+    private AbstractAdapter[] m_adapters;
+
+    private ServiceRegistration m_managedServiceReg;
+
+    public Configuration( BundleContext bundleContext )
+    {
+        m_bundleContext = bundleContext;
+
+        // default configuration
+        configure( null );
+        startOrUpdate();
+
+        // check for Configuration Admin configuration
+        try
+        {
+            Object service = tryToCreateManagedService();
+            if ( service != null )
+            {
+                // add meta type provider if interfaces are available
+                Object enhancedService = tryToCreateMetaTypeProvider(service);
+                final String[] interfaceNames;
+                if ( enhancedService == null )
+                {
+                    interfaceNames = new String[] {ManagedService.class.getName()};
+                }
+                else
+                {
+                    interfaceNames = new String[] {ManagedService.class.getName(), MetaTypeProvider.class.getName()};
+                    service = enhancedService;
+                }
+                Dictionary props = new Hashtable();
+                props.put( Constants.SERVICE_PID, PID );
+                m_managedServiceReg = m_bundleContext.registerService( interfaceNames, service, props );
+            }
+        }
+        catch ( Throwable t )
+        {
+            // don't care
+        }
+    }
+
+    void updateFromConfigAdmin(final Dictionary config)
+    {
+        // do this in the background as we don't want to stop
+        // the config admin
+        new Thread()
+        {
+
+            public void run()
+            {
+                synchronized ( Configuration.this )
+                {
+                    Configuration.this.configure( config );
+                    Configuration.this.startOrUpdate();
+                }
+            }
+
+        }.start();
+
+    }
+
+    /**
+     * Configures this instance.
+     */
+    void configure( Dictionary config )
+    {
+        if ( config == null )
+        {
+            // 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.
+            m_threadPoolSize = getIntProperty(
+                    PROP_THREAD_POOL_SIZE, m_bundleContext.getProperty(PROP_THREAD_POOL_SIZE), 20, 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.
+            m_timeout = getIntProperty(PROP_TIMEOUT,
+                    m_bundleContext.getProperty(PROP_TIMEOUT), 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=*).
+            m_requireTopic = getBooleanProperty(
+                    m_bundleContext.getProperty(PROP_REQUIRE_TOPIC), true);
+            final String value = m_bundleContext.getProperty(PROP_IGNORE_TIMEOUT);
+            if ( value == null )
+            {
+                m_ignoreTimeout = null;
+            }
+            else
+            {
+                final StringTokenizer st = new StringTokenizer(value, ",");
+                m_ignoreTimeout = new String[st.countTokens()];
+                for(int i=0; i<m_ignoreTimeout.length; i++)
+                {
+                    m_ignoreTimeout[i] = st.nextToken();
+                }
+            }
+            m_logLevel = getIntProperty(PROP_LOG_LEVEL,
+                    m_bundleContext.getProperty(PROP_LOG_LEVEL),
+                    LogWrapper.LOG_WARNING, // default log level is WARNING
+                    LogWrapper.LOG_ERROR);
+            m_addTimestamp = getBooleanProperty(
+                    m_bundleContext.getProperty(PROP_ADD_TIMESTAMP), false);
+            m_addSubject = getBooleanProperty(
+                    m_bundleContext.getProperty(PROP_ADD_SUBJECT), false);
+        }
+        else
+        {
+            m_threadPoolSize = getIntProperty(PROP_THREAD_POOL_SIZE, config.get(PROP_THREAD_POOL_SIZE), 20, 2);
+            m_timeout = getIntProperty(PROP_TIMEOUT, config.get(PROP_TIMEOUT), 5000, Integer.MIN_VALUE);
+            m_requireTopic = getBooleanProperty(config.get(PROP_REQUIRE_TOPIC), true);
+            m_ignoreTimeout = null;
+            final Object value = config.get(PROP_IGNORE_TIMEOUT);
+            if ( value instanceof String )
+            {
+                m_ignoreTimeout = new String[] {(String)value};
+            }
+            else if ( value instanceof String[] )
+            {
+                m_ignoreTimeout = (String[])value;
+            }
+            else
+            {
+                LogWrapper.getLogger().log(LogWrapper.LOG_WARNING,
+                        "Value for property: " + PROP_IGNORE_TIMEOUT + " is neither a string nor a string array - Using default");
+            }
+            m_logLevel = getIntProperty(PROP_LOG_LEVEL,
+                    config.get(PROP_LOG_LEVEL),
+                    LogWrapper.LOG_WARNING, // default log level is WARNING
+                    LogWrapper.LOG_ERROR);
+            m_addTimestamp = getBooleanProperty(
+                    config.get(PROP_ADD_TIMESTAMP), false);
+            m_addSubject = getBooleanProperty(
+                    config.get(PROP_ADD_SUBJECT), false);
+        }
+        // a timeout less or equals to 100 means : disable timeout
+        if ( m_timeout <= 100 )
+        {
+            m_timeout = 0;
+        }
+    }
+
+    private void startOrUpdate()
+    {
+        LogWrapper.getLogger().setLogLevel(m_logLevel);
+        LogWrapper.getLogger().log(LogWrapper.LOG_DEBUG,
+                PROP_LOG_LEVEL + "=" + m_logLevel);
+        LogWrapper.getLogger().log(LogWrapper.LOG_DEBUG,
+                PROP_THREAD_POOL_SIZE + "=" + m_threadPoolSize);
+        LogWrapper.getLogger().log(LogWrapper.LOG_DEBUG,
+                PROP_TIMEOUT + "=" + m_timeout);
+        LogWrapper.getLogger().log(LogWrapper.LOG_DEBUG,
+                PROP_REQUIRE_TOPIC + "=" + m_requireTopic);
+
+        // 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.
+        if ( m_sync_pool == null )
+        {
+            m_sync_pool = new DefaultThreadPool(m_threadPoolSize, true);
+        }
+        else
+        {
+            m_sync_pool.configure(m_threadPoolSize);
+        }
+        final int asyncThreadPoolSize = m_threadPoolSize > 5 ? m_threadPoolSize / 2 : 2;
+        if ( m_async_pool == null )
+        {
+            m_async_pool = new DefaultThreadPool(asyncThreadPoolSize, false);
+        }
+        else
+        {
+            m_async_pool.configure(asyncThreadPoolSize);
+        }
+
+        if ( m_admin == null )
+        {
+            m_admin = new EventAdminImpl(m_bundleContext,
+                    m_sync_pool,
+                    m_async_pool,
+                    m_timeout,
+                    m_ignoreTimeout,
+                    m_requireTopic,
+                    m_addTimestamp,
+                    m_addSubject);
+
+            // Finally, adapt the outside events to our kind of events as per spec
+            adaptEvents(m_admin);
+
+            // 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
+            m_registration = m_bundleContext.registerService(EventAdmin.class.getName(),
+                    new SecureEventAdminFactory(m_admin), null);
+        }
+        else
+        {
+            m_admin.update(m_timeout, m_ignoreTimeout, m_requireTopic, m_addTimestamp, m_addSubject);
+        }
+
+    }
+
+    /**
+     * Called upon stopping the bundle. This will block until all pending events are
+     * delivered. An IllegalStateException will be thrown on new events starting with
+     * the begin of this method. However, it might take some time until we settle
+     * down which is somewhat cumbersome given that the spec asks for return in
+     * a timely manner.
+     */
+    public void destroy()
+    {
+        synchronized ( this )
+        {
+            if ( m_adapters != null )
+            {
+                for(int i=0;i<m_adapters.length;i++)
+                {
+                    m_adapters[i].destroy(m_bundleContext);
+                }
+                m_adapters = null;
+            }
+            if ( m_managedServiceReg != null )
+            {
+                m_managedServiceReg.unregister();
+                m_managedServiceReg = null;
+            }
+            // We need to unregister manually
+            if ( m_registration != null )
+            {
+                m_registration.unregister();
+                m_registration = null;
+            }
+            if ( m_admin != null )
+            {
+                m_admin.stop();
+                m_admin = null;
+            }
+            if (m_async_pool != null )
+            {
+                m_async_pool.close();
+                m_async_pool = null;
+            }
+            if ( m_sync_pool != null )
+            {
+                m_sync_pool.close();
+                m_sync_pool = null;
+            }
+        }
+    }
+
+    /**
+     * Init the adapters in org.apache.felix.eventadmin.impl.adapter
+     */
+    private void adaptEvents(final EventAdmin admin)
+    {
+        m_adapters = new AbstractAdapter[4];
+        m_adapters[0] = new FrameworkEventAdapter(m_bundleContext, admin);
+        m_adapters[1] = new BundleEventAdapter(m_bundleContext, admin);
+        m_adapters[2] = new ServiceEventAdapter(m_bundleContext, admin);
+        m_adapters[3] = new LogEventAdapter(m_bundleContext, admin);
+    }
+
+    private Object tryToCreateMetaTypeProvider(final Object managedService)
+    {
+        try
+        {
+            return new MetaTypeProviderImpl((ManagedService)managedService,
+                    m_threadPoolSize, m_timeout, m_requireTopic,
+                    m_ignoreTimeout);
+        }
+        catch (final Throwable t)
+        {
+            // we simply ignore this
+        }
+        return null;
+    }
+
+    private Object tryToCreateManagedService()
+    {
+        try
+        {
+            return new ManagedService()
+            {
+                public void updated( Dictionary properties ) throws ConfigurationException
+                {
+                    updateFromConfigAdmin(properties);
+                }
+            };
+        }
+        catch (Throwable t)
+        {
+            // we simply ignore this
+        }
+        return null;
+    }
+
+    /**
+     * 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 Object value,
+                               final int defaultValue, final int min)
+    {
+        if(null != value)
+        {
+            final int result;
+            if ( value instanceof Integer )
+            {
+                result = ((Integer)value).intValue();
+            }
+            else
+            {
+                try
+                {
+                    result = Integer.parseInt(value.toString());
+                }
+                catch (NumberFormatException e)
+                {
+                    LogWrapper.getLogger().log(LogWrapper.LOG_WARNING,
+                            "Unable to parse property: " + key + " - Using default", e);
+                    return defaultValue;
+                }
+            }
+            if(result >= min)
+            {
+                return result;
+            }
+
+            LogWrapper.getLogger().log(LogWrapper.LOG_WARNING,
+                    "Value for property: " + key + " is to low - Using default");
+        }
+
+        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 Object obj,
+                                       final boolean defaultValue)
+    {
+        if(null != obj)
+        {
+            if ( obj instanceof Boolean )
+            {
+                return ((Boolean)obj).booleanValue();
+            }
+            String value = obj.toString().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;
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2fe4f77b/services/eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/EventAdminImpl.java
----------------------------------------------------------------------
diff --git a/services/eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/EventAdminImpl.java b/services/eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/EventAdminImpl.java
new file mode 100644
index 0000000..5020c49
--- /dev/null
+++ b/services/eventadmin/src/main/java/org/apache/felix/eventadmin/impl/handler/EventAdminImpl.java
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.eventadmin.impl.handler;
+
+import java.security.AccessController;
+import java.util.HashMap;
+
+import javax.security.auth.Subject;
+
+import org.apache.felix.eventadmin.impl.tasks.AsyncDeliverTasks;
+import org.apache.felix.eventadmin.impl.tasks.DefaultThreadPool;
+import org.apache.felix.eventadmin.impl.tasks.SyncDeliverTasks;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.service.event.EventConstants;
+
+/**
+ * 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:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class EventAdminImpl implements EventAdmin
+{
+    /** The tracker for the event handlers. */
+    private volatile EventHandlerTracker tracker;
+
+    // The asynchronous event dispatcher
+    private final AsyncDeliverTasks m_postManager;
+
+    // The synchronous event dispatcher
+    private final SyncDeliverTasks m_sendManager;
+
+    private boolean addTimestamp;
+    private boolean addSubject;
+
+    /**
+     * The constructor of the <tt>EventAdmin</tt> implementation.
+     *
+     * @param syncPool The synchronous thread pool
+     * @param asyncPool The asynchronous thread pool
+     */
+    public EventAdminImpl(
+            final BundleContext bundleContext,
+            final DefaultThreadPool syncPool,
+            final DefaultThreadPool asyncPool,
+            final int timeout,
+            final String[] ignoreTimeout,
+            final boolean requireTopic,
+            final boolean addTimestamp,
+            final boolean addSubject)
+    {
+        checkNull(syncPool, "syncPool");
+        checkNull(asyncPool, "asyncPool");
+
+        this.addTimestamp = addTimestamp;
+        this.addSubject = addSubject;
+        this.tracker = new EventHandlerTracker(bundleContext);
+        this.tracker.update(ignoreTimeout, requireTopic);
+        this.tracker.open();
+        m_sendManager = new SyncDeliverTasks(syncPool, timeout);
+        m_postManager = new AsyncDeliverTasks(asyncPool, m_sendManager);
+    }
+
+    /**
+     * Check if the event admin is active and return the tracker
+     * @return The tracker
+     * @throws IllegalArgumentException if the event admin has been stopped
+     */
+    private EventHandlerTracker getTracker() {
+        final EventHandlerTracker localTracker = tracker;
+        if ( localTracker == null ) {
+            throw new IllegalStateException("The EventAdmin is stopped");
+        }
+        return localTracker;
+    }
+
+    /**
+     * 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)
+    {
+        m_postManager.execute(this.getTracker().getHandlers(event), prepareEvent(event));
+    }
+
+    static final String SUBJECT = "subject";
+
+    private Event prepareEvent(Event event) {
+        boolean needTimeStamp = addTimestamp && !event.containsProperty(EventConstants.TIMESTAMP);
+        boolean needSubject = addSubject && !event.containsProperty(SUBJECT);
+        Subject subject = null;
+        if (needSubject) {
+            subject = Subject.getSubject(AccessController.getContext());
+            needSubject = (subject != null);
+        }
+        if (needTimeStamp || needSubject) {
+            String[] names = event.getPropertyNames();
+            HashMap map = new HashMap(names.length + 1);
+            for (int i = 0; i < names.length; i++) {
+                if (!EventConstants.EVENT_TOPIC.equals(names[i])) {
+                    map.put(names[i], event.getProperty(names[i]));
+                }
+            }
+            if (needTimeStamp) {
+                map.put(EventConstants.TIMESTAMP, new Long(System.currentTimeMillis()));
+            }
+            if (needSubject) {
+                map.put(SUBJECT, subject);
+            }
+            event = new Event(event.getTopic(), map);
+        }
+        return event;
+    }
+
+    /**
+     * 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)
+    {
+        m_sendManager.execute(this.getTracker().getHandlers(event), prepareEvent(event), false);
+    }
+
+    /**
+     * This method can be used to stop the delivery of events.
+     */
+    public void stop()
+    {
+        this.tracker.close();
+        this.tracker = null;
+    }
+
+    /**
+     * Update the event admin with new configuration.
+     */
+    public void update(final int timeout,
+                       final String[] ignoreTimeout,
+                       final boolean requireTopic,
+                       final boolean addTimestamp,
+                       final boolean addSubject)
+    {
+        this.addTimestamp = addTimestamp;
+        this.addSubject = addSubject;
+        this.tracker.close();
+        this.tracker.update(ignoreTimeout, requireTopic);
+        this.m_sendManager.update(timeout);
+        this.tracker.open();
+    }
+
+    /**
+     * 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");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2fe4f77b/services/pom.xml
----------------------------------------------------------------------
diff --git a/services/pom.xml b/services/pom.xml
new file mode 100644
index 0000000..cd6eaec
--- /dev/null
+++ b/services/pom.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <!--
+
+        Licensed to the Apache Software Foundation (ASF) under one or more
+        contributor license agreements.  See the NOTICE file distributed with
+        this work for additional information regarding copyright ownership.
+        The ASF licenses this file to You under the Apache License, Version 2.0
+        (the "License"); you may not use this file except in compliance with
+        the License.  You may obtain a copy of the License at
+
+           http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.karaf</groupId>
+        <artifactId>karaf</artifactId>
+        <version>3.1.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <groupId>org.apache.karaf.services</groupId>
+    <artifactId>services</artifactId>
+    <packaging>pom</packaging>
+    <name>Apache Karaf :: OSGi Services</name>
+
+    <modules>
+        <module>eventadmin</module>
+    </modules>
+
+</project>
+