FELIX-4604 : Add a configuration to ignore certain events

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1617208 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/eventadmin/impl/changelog.txt b/eventadmin/impl/changelog.txt
index fe8532d..2fdf46e 100644
--- a/eventadmin/impl/changelog.txt
+++ b/eventadmin/impl/changelog.txt
@@ -1,6 +1,7 @@
 Changes from 1.3.2 to 1.3.4
 ---------------------------
 ** Improvement
+    * [FELIX-4604] - Add a configuration to ignore certain events
     * [FELIX-3511] - Use java.concurrent from Java 6
     * [FELIX-4316] - Packages imported dynamically should also be imported statically with an optional flag
 
diff --git a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/Configuration.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/Configuration.java
index 8661aea..690ca3e 100644
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/Configuration.java
+++ b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/Configuration.java
@@ -81,20 +81,43 @@
  *      <tt>org.apache.felix.eventadmin.IgnoreTimeout</tt> - Configure
  *         <tt>EventHandler</tt>s to be called without a timeout.
  * </p>
+ * <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,
+ * </p>
+ * <p>
+ * The value is a list of strings (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.
- *
+ * </p>
+ * <p>
+ * <p>
+ *      <tt>org.apache.felix.eventadmin.IgnoreTopic</tt> - Configure
+ *         topics to be ignore and not delivered to registered handlers.
+ * </p>
+ * <p>
+ * For performance optimization it is possible to configure topics which are ignored
+ * by the event admin implementation. In this case, a event is not delivered to
+ * registered event handlers.
+ * </p>
+ * <p>
+ * The value is a list of strings (separated by comma). If a single value ends with a dot,
+ * all topics in exactly this package are ignored. If a single value ends with a star,
+ * all topics in this package and all sub packages are ignored. If a single value neither
+ * ends with a dot nor with a start, this is assumed to define an exact topic. A single
+ * star can be used to disable delivery completely.
+ * </p>
+ * <p>
+ * <p>
  * 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.
+ * </p>
  *
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
@@ -107,6 +130,7 @@
     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_IGNORE_TOPIC = "org.apache.felix.eventadmin.IgnoreTopic";
     static final String PROP_LOG_LEVEL = "org.apache.felix.eventadmin.LogLevel";
 
     /** The bundle context. */
@@ -120,6 +144,8 @@
 
     private String[] m_ignoreTimeout;
 
+    private String[] m_ignoreTopics;
+
     private int m_logLevel;
 
     // The thread pool used - this is a member because we need to close it on stop
@@ -244,6 +270,21 @@
                     m_ignoreTimeout[i] = st.nextToken();
                 }
             }
+
+            final String valueIgnoreTopic = m_bundleContext.getProperty(PROP_IGNORE_TOPIC);
+            if ( valueIgnoreTopic == null )
+            {
+                m_ignoreTopics = null;
+            }
+            else
+            {
+                final StringTokenizer st = new StringTokenizer(valueIgnoreTopic, ",");
+                m_ignoreTopics = new String[st.countTokens()];
+                for(int i=0; i<m_ignoreTopics.length; i++)
+                {
+                    m_ignoreTopics[i] = st.nextToken();
+                }
+            }
             m_logLevel = getIntProperty(PROP_LOG_LEVEL,
                     m_bundleContext.getProperty(PROP_LOG_LEVEL),
                     LogWrapper.LOG_WARNING, // default log level is WARNING
@@ -269,6 +310,21 @@
                 LogWrapper.getLogger().log(LogWrapper.LOG_WARNING,
                         "Value for property: " + PROP_IGNORE_TIMEOUT + " is neither a string nor a string array - Using default");
             }
+            m_ignoreTopics = null;
+            final Object valueIT = config.get(PROP_IGNORE_TOPIC);
+            if ( valueIT instanceof String )
+            {
+                m_ignoreTopics = new String[] {(String)valueIT};
+            }
+            else if ( valueIT instanceof String[] )
+            {
+                m_ignoreTopics = (String[])valueIT;
+            }
+            else
+            {
+                LogWrapper.getLogger().log(LogWrapper.LOG_WARNING,
+                        "Value for property: " + PROP_IGNORE_TOPIC + " 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
@@ -322,7 +378,8 @@
                     m_async_pool,
                     m_timeout,
                     m_ignoreTimeout,
-                    m_requireTopic);
+                    m_requireTopic,
+                    m_ignoreTopics);
 
             // Finally, adapt the outside events to our kind of events as per spec
             adaptEvents(m_admin);
@@ -335,7 +392,7 @@
         }
         else
         {
-            m_admin.update(m_timeout, m_ignoreTimeout, m_requireTopic);
+            m_admin.update(m_timeout, m_ignoreTimeout, m_requireTopic, m_ignoreTopics);
         }
 
     }
@@ -406,7 +463,7 @@
         {
             return new MetaTypeProviderImpl((ManagedService)managedService,
                     m_threadPoolSize, m_timeout, m_requireTopic,
-                    m_ignoreTimeout);
+                    m_ignoreTimeout, m_ignoreTopics);
         }
         catch (final Throwable t)
         {
diff --git a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/MetaTypeProviderImpl.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/MetaTypeProviderImpl.java
index 5b6f939..8212411 100644
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/MetaTypeProviderImpl.java
+++ b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/MetaTypeProviderImpl.java
@@ -40,19 +40,22 @@
     private final int m_timeout;
     private final boolean m_requireTopic;
     private final String[] m_ignoreTimeout;
+    private final String[] m_ignoreTopic;
 
     private final ManagedService m_delegatee;
 
     public MetaTypeProviderImpl(final ManagedService delegatee,
             final int threadPoolSize,
             final int timeout, final boolean requireTopic,
-            final String[] ignoreTimeout)
+            final String[] ignoreTimeout,
+            final String[] ignoreTopic)
     {
         m_threadPoolSize = threadPoolSize;
         m_timeout = timeout;
         m_requireTopic = requireTopic;
         m_delegatee = delegatee;
         m_ignoreTimeout = ignoreTimeout;
+        m_ignoreTopic = ignoreTopic;
     }
 
     private ObjectClassDefinition ocd;
@@ -121,6 +124,15 @@
                     "all handlers in this package and all subpackages are ignored. If the string neither " +
                     "ends with a dot nor with a star, this is assumed to define an exact class name.",
                     AttributeDefinition.STRING, m_ignoreTimeout, Integer.MAX_VALUE, null, null));
+            adList.add( new AttributeDefinitionImpl( Configuration.PROP_IGNORE_TIMEOUT, "Ignore Topics",
+                    "For performance optimization it is possible to configure topics which are ignored " +
+                    "by the event admin implementation. In this case, a event is not delivered to " +
+                    "registered event handlers. The value is a list of strings (separated by comma). " +
+                    "If a single value ends with a dot, all topics in exactly this package are ignored. " +
+                    "If a single value ends with a star, all topics in this package and all sub packages " +
+                    "are ignored. If a single value neither ends with a dot nor with a start, this is assumed " +
+                    "to define an exact topic. A single star can be used to disable delivery completely.",
+                    AttributeDefinition.STRING, m_ignoreTopic, Integer.MAX_VALUE, null, null));
             ocd = new ObjectClassDefinition()
             {
 
diff --git a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/EventAdminImpl.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/EventAdminImpl.java
index 3b33bde..8750818 100644
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/EventAdminImpl.java
+++ b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/EventAdminImpl.java
@@ -18,6 +18,7 @@
  */
 package org.apache.felix.eventadmin.impl.handler;
 
+import org.apache.felix.eventadmin.impl.handler.EventHandlerTracker.Matcher;
 import org.apache.felix.eventadmin.impl.tasks.AsyncDeliverTasks;
 import org.apache.felix.eventadmin.impl.tasks.DefaultThreadPool;
 import org.apache.felix.eventadmin.impl.tasks.SyncDeliverTasks;
@@ -49,6 +50,9 @@
     // The synchronous event dispatcher
     private final SyncDeliverTasks m_sendManager;
 
+    // matchers for ignore topics
+    private Matcher[] m_ignoreTopics;
+
     /**
      * The constructor of the <tt>EventAdmin</tt> implementation.
      *
@@ -61,7 +65,8 @@
                     final DefaultThreadPool asyncPool,
                     final int timeout,
                     final String[] ignoreTimeout,
-                    final boolean requireTopic)
+                    final boolean requireTopic,
+                    final String[] ignoreTopics)
     {
         checkNull(syncPool, "syncPool");
         checkNull(asyncPool, "asyncPool");
@@ -71,6 +76,7 @@
         this.tracker.open();
         m_sendManager = new SyncDeliverTasks(syncPool, timeout);
         m_postManager = new AsyncDeliverTasks(asyncPool, m_sendManager);
+        m_ignoreTopics = EventHandlerTracker.createMatchers(ignoreTopics);
     }
 
     /**
@@ -87,6 +93,26 @@
     }
 
     /**
+     * Check whether the topic should be delivered at all
+     */
+    private boolean checkTopic( final Event event )
+    {
+        boolean result = true;
+        if ( this.m_ignoreTopics != null )
+        {
+            for(final Matcher m : this.m_ignoreTopics)
+            {
+                if ( m.match(event.getTopic()) )
+                {
+                    result = false;
+                    break;
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
      * Post an asynchronous event.
      *
      * @param event The event to be posted by this service
@@ -95,9 +121,13 @@
      *
      * @see org.osgi.service.event.EventAdmin#postEvent(org.osgi.service.event.Event)
      */
+    @Override
     public void postEvent(final Event event)
     {
-        m_postManager.execute(this.getTracker().getHandlers(event), event);
+        if ( checkTopic(event) )
+        {
+            m_postManager.execute(this.getTracker().getHandlers(event), event);
+        }
     }
 
     /**
@@ -109,9 +139,13 @@
      *
      * @see org.osgi.service.event.EventAdmin#sendEvent(org.osgi.service.event.Event)
      */
+    @Override
     public void sendEvent(final Event event)
     {
-        m_sendManager.execute(this.getTracker().getHandlers(event), event, false);
+        if ( checkTopic(event) )
+        {
+            m_sendManager.execute(this.getTracker().getHandlers(event), event, false);
+        }
     }
 
     /**
@@ -128,12 +162,14 @@
      */
     public void update(final int timeout,
                     final String[] ignoreTimeout,
-                    final boolean requireTopic)
+                    final boolean requireTopic,
+                    final String[] ignoreTopics)
     {
         this.tracker.close();
         this.tracker.update(ignoreTimeout, requireTopic);
         this.m_sendManager.update(timeout);
         this.tracker.open();
+        this.m_ignoreTopics = EventHandlerTracker.createMatchers(ignoreTopics);
     }
 
     /**
diff --git a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/EventHandlerTracker.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/EventHandlerTracker.java
index cf76a7c..077f32c 100644
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/EventHandlerTracker.java
+++ b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/EventHandlerTracker.java
@@ -274,6 +274,47 @@
 	    }
 	}
 
+	static Matcher[] createMatchers(final String[] config)
+	{
+        final Matcher[] matchers;
+        if ( config == null || config.length == 0 )
+        {
+            matchers = null;
+        }
+        else
+        {
+            matchers = new Matcher[config.length];
+            for(int i=0;i<config.length;i++)
+            {
+                String value = config[i];
+                if ( value != null )
+                {
+                    value = value.trim();
+                }
+                if ( value != null && value.length() > 0 )
+                {
+                    if ( value.endsWith(".") )
+                    {
+                        matchers[i] = new PackageMatcher(value.substring(0, value.length() - 1));
+                    }
+                    else if ( value.endsWith("*") )
+                    {
+                        if ( value.equals("*") )
+                        {
+                            return new Matcher[] {new MatcherAll()};
+                        }
+                        matchers[i] = new SubPackageMatcher(value.substring(0, value.length() - 1));
+                    }
+                    else
+                    {
+                        matchers[i] = new ClassMatcher(value);
+                    }
+                }
+            }
+        }
+        return matchers;
+	}
+
     /**
      * The matcher interface for checking if timeout handling
      * is disabled for the handler.
@@ -284,6 +325,16 @@
         boolean match(String className);
     }
 
+    /** Match all. */
+    private static final class MatcherAll implements Matcher
+    {
+        @Override
+        public boolean match(final String className)
+        {
+            return true;
+        }
+    }
+
     /** Match a package. */
     private static final class PackageMatcher implements Matcher
     {
@@ -294,7 +345,7 @@
             m_packageName = name;
         }
         @Override
-        public boolean match(String className)
+        public boolean match(final String className)
         {
             final int pos = className.lastIndexOf('.');
             return pos > -1 && className.substring(0, pos).equals(m_packageName);
@@ -311,7 +362,7 @@
             m_packageName = name + '.';
         }
         @Override
-        public boolean match(String className)
+        public boolean match(final String className)
         {
             final int pos = className.lastIndexOf('.');
             return pos > -1 && className.substring(0, pos + 1).startsWith(m_packageName);
@@ -328,7 +379,7 @@
             m_className = name;
         }
         @Override
-        public boolean match(String className)
+        public boolean match(final String className)
         {
             return m_className.equals(className);
         }