FELIX-1960 : Fine-grained timeout configuration
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@906102 13f79535-47bb-0310-9956-ffa450edef68
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 8a042cc..a193042 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
@@ -19,8 +19,7 @@
package org.apache.felix.eventadmin.impl;
-import java.util.Dictionary;
-import java.util.Hashtable;
+import java.util.*;
import org.apache.felix.eventadmin.impl.adapter.*;
import org.apache.felix.eventadmin.impl.dispatch.DefaultThreadPool;
@@ -81,6 +80,21 @@
* <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
@@ -95,6 +109,7 @@
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";
/** The bundle context. */
private final BundleContext m_bundleContext;
@@ -107,6 +122,8 @@
private boolean m_requireTopic;
+ private String[] m_ignoreTimeout;
+
// The thread pool used - this is a member because we need to close it on stop
private volatile ThreadPool m_sync_pool;
@@ -206,6 +223,19 @@
// (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();
+ }
+ }
}
else
{
@@ -213,6 +243,21 @@
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");
+ }
}
}
@@ -255,7 +300,9 @@
m_sync_pool = new DefaultThreadPool(m_threadPoolSize, true);
m_async_pool = new DefaultThreadPool(m_threadPoolSize > 5 ? m_threadPoolSize / 2 : 2, false);
- final DeliverTask syncExecuter = new SyncDeliverTasks(m_sync_pool, (m_timeout > 100 ? m_timeout : 0));
+ final DeliverTask syncExecuter = new SyncDeliverTasks(m_sync_pool,
+ (m_timeout > 100 ? m_timeout : 0),
+ m_ignoreTimeout);
m_admin = createEventAdmin(m_bundleContext,
handlerTasks,
new AsyncDeliverTasks(m_async_pool, syncExecuter),
@@ -366,7 +413,8 @@
try
{
return new MetaTypeProviderImpl((ManagedService)managedService,
- m_cacheSize, m_threadPoolSize, m_timeout, m_requireTopic);
+ m_cacheSize, m_threadPoolSize, m_timeout, m_requireTopic,
+ m_ignoreTimeout);
} catch (Throwable t)
{
// we simply ignore this
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 3605460..736a2f6 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
@@ -36,18 +36,21 @@
private final int m_threadPoolSize;
private final int m_timeout;
private final boolean m_requireTopic;
+ private final String[] m_ignoreTimeout;
private final ManagedService m_delegatee;
public MetaTypeProviderImpl(final ManagedService delegatee,
final int cacheSize, final int threadPoolSize,
- final int timeout, final boolean requireTopic)
+ final int timeout, final boolean requireTopic,
+ final String[] ignoreTimeout)
{
m_cacheSize = cacheSize;
m_threadPoolSize = threadPoolSize;
m_timeout = timeout;
m_requireTopic = requireTopic;
m_delegatee = delegatee;
+ m_ignoreTimeout = ignoreTimeout;
}
private ObjectClassDefinition ocd;
@@ -107,7 +110,17 @@
"will enable that handlers without a topic are receiving all events " +
"(i.e., they are treated the same as with a topic=*).",
m_requireTopic ) );
-
+ adList.add( new AttributeDefinitionImpl( Configuration.PROP_IGNORE_TIMEOUT, "Ignore Timeouts",
+ "Configure event handlers to be called without a timeout. 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 strings. If a 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.",
+ AttributeDefinition.STRING, m_ignoreTimeout, Integer.MAX_VALUE, null, null));
ocd = new ObjectClassDefinition()
{
diff --git a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/tasks/HandlerTask.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/tasks/HandlerTask.java
index 33289a0..668f333 100644
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/tasks/HandlerTask.java
+++ b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/tasks/HandlerTask.java
@@ -27,6 +27,11 @@
public interface HandlerTask
{
/**
+ * Return the class name of the handler
+ */
+ String getHandlerClassName();
+
+ /**
* Deliver the event to the handler.
*/
void execute();
diff --git a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/tasks/HandlerTaskImpl.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/tasks/HandlerTaskImpl.java
index 213620e..16b975f 100644
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/tasks/HandlerTaskImpl.java
+++ b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/tasks/HandlerTaskImpl.java
@@ -62,6 +62,18 @@
}
/**
+ * @see org.apache.felix.eventadmin.impl.tasks.HandlerTask#getHandlerClassName()
+ */
+ public String getHandlerClassName() {
+ final EventHandler handler = m_handlerTasks.getEventHandler(m_eventHandlerRef);
+ try {
+ return handler.getClass().getName();
+ } finally {
+ m_handlerTasks.ungetEventHandler(handler, m_eventHandlerRef);
+ }
+ }
+
+ /**
* @see org.apache.felix.eventadmin.impl.tasks.HandlerTask#execute()
*/
public void execute()
diff --git a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/tasks/SyncDeliverTasks.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/tasks/SyncDeliverTasks.java
index 3b674c3..a27146a 100644
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/tasks/SyncDeliverTasks.java
+++ b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/tasks/SyncDeliverTasks.java
@@ -57,15 +57,95 @@
/** The timeout for event handlers, 0 = disabled. */
final long m_timeout;
+ private static interface Matcher
+ {
+ boolean match(String className);
+ }
+ private static final class PackageMatcher implements Matcher
+ {
+ private final String m_packageName;
+
+ public PackageMatcher(final String name)
+ {
+ m_packageName = name;
+ }
+ public boolean match(String className)
+ {
+ final int pos = className.lastIndexOf('.');
+ return pos > -1 && className.substring(0, pos).equals(m_packageName);
+ }
+ }
+ private static final class SubPackageMatcher implements Matcher
+ {
+ private final String m_packageName;
+
+ public SubPackageMatcher(final String name)
+ {
+ m_packageName = name + '.';
+ }
+ public boolean match(String className)
+ {
+ final int pos = className.lastIndexOf('.');
+ return pos > -1 && className.substring(0, pos + 1).startsWith(m_packageName);
+ }
+ }
+ private static final class ClassMatcher implements Matcher
+ {
+ private final String m_className;
+
+ public ClassMatcher(final String name)
+ {
+ m_className = name;
+ }
+ public boolean match(String className)
+ {
+ return m_className.equals(className);
+ }
+ }
+
+ /** The matchers for ignore timeout handling. */
+ private final Matcher[] m_ignoreTimeoutMatcher;
+
/**
* Construct a new sync deliver tasks.
* @param pool The thread pool used to spin-off new threads.
* @param timeout The timeout for an event handler, 0 = disabled
*/
- public SyncDeliverTasks(final ThreadPool pool, final long timeout)
+ public SyncDeliverTasks(final ThreadPool pool, final long timeout, final String[] ignoreTimeout)
{
m_pool = pool;
m_timeout = timeout;
+ if ( ignoreTimeout == null || ignoreTimeout.length == 0 )
+ {
+ m_ignoreTimeoutMatcher = null;
+ }
+ else
+ {
+ m_ignoreTimeoutMatcher = new Matcher[ignoreTimeout.length];
+ for(int i=0;i<ignoreTimeout.length;i++)
+ {
+ String value = ignoreTimeout[i];
+ if ( value != null )
+ {
+ value = value.trim();
+ }
+ if ( value != null && value.length() > 0 )
+ {
+ if ( value.endsWith(".") )
+ {
+ m_ignoreTimeoutMatcher[i] = new PackageMatcher(value.substring(0, value.length() - 1));
+ }
+ else if ( value.endsWith("*") )
+ {
+ m_ignoreTimeoutMatcher[i] = new SubPackageMatcher(value.substring(0, value.length() - 1));
+ }
+ else
+ {
+ m_ignoreTimeoutMatcher[i] = new ClassMatcher(value);
+ }
+ }
+ }
+ }
}
/**
@@ -75,7 +155,26 @@
*/
private boolean useTimeout(final HandlerTask task)
{
- return m_timeout > 0;
+ // we only check the classname if a timeout is configured
+ if ( m_timeout > 0)
+ {
+ if ( m_ignoreTimeoutMatcher != null )
+ {
+ final String className = task.getHandlerClassName();
+ for(int i=0;i<m_ignoreTimeoutMatcher.length;i++)
+ {
+ if ( m_ignoreTimeoutMatcher[i] != null)
+ {
+ if ( m_ignoreTimeoutMatcher[i].match(className) )
+ {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+ return false;
}
/**