FELIX-3321 : Improve implementation and reduce load on the service registry
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1236655 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 858b092..ee95279 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
@@ -22,10 +22,9 @@
import java.util.*;
import org.apache.felix.eventadmin.impl.adapter.*;
-import org.apache.felix.eventadmin.impl.dispatch.DefaultThreadPool;
-import org.apache.felix.eventadmin.impl.handler.*;
+import org.apache.felix.eventadmin.impl.handler.EventAdminImpl;
import org.apache.felix.eventadmin.impl.security.SecureEventAdminFactory;
-import org.apache.felix.eventadmin.impl.util.LeastRecentlyUsedCacheMap;
+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;
@@ -41,14 +40,6 @@
* 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>
@@ -96,13 +87,14 @@
* 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_CACHE_SIZE = "org.apache.felix.eventadmin.CacheSize";
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";
@@ -112,8 +104,6 @@
/** The bundle context. */
private final BundleContext m_bundleContext;
- private int m_cacheSize;
-
private int m_threadPoolSize;
private int m_timeout;
@@ -207,14 +197,6 @@
{
if ( config == null )
{
- // 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.
- m_cacheSize = getIntProperty(PROP_CACHE_SIZE,
- m_bundleContext.getProperty(PROP_CACHE_SIZE), 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.
@@ -260,7 +242,6 @@
}
else
{
- m_cacheSize = getIntProperty(PROP_CACHE_SIZE, config.get(PROP_CACHE_SIZE), 30, 10);
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);
@@ -297,21 +278,12 @@
LogWrapper.getLogger().log(LogWrapper.LOG_DEBUG,
PROP_LOG_LEVEL + "=" + m_logLevel);
LogWrapper.getLogger().log(LogWrapper.LOG_DEBUG,
- PROP_CACHE_SIZE + "=" + m_cacheSize);
- 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);
- final TopicHandlerFilters topicHandlerFilters =
- new CacheTopicHandlerFilters(new LeastRecentlyUsedCacheMap(m_cacheSize),
- m_requireTopic);
-
- final Filters filters = new CacheFilters(
- new LeastRecentlyUsedCacheMap(m_cacheSize), m_bundleContext);
-
// 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
@@ -334,16 +306,14 @@
m_async_pool.configure(asyncThreadPoolSize);
}
- // 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(m_bundleContext,
- new CleanBlackList(), topicHandlerFilters, filters);
-
if ( m_admin == null )
{
- m_admin = new EventAdminImpl(handlerTasks, m_sync_pool, m_async_pool, m_timeout, m_ignoreTimeout);
+ m_admin = new EventAdminImpl(m_bundleContext,
+ m_sync_pool,
+ m_async_pool,
+ m_timeout,
+ m_ignoreTimeout,
+ m_requireTopic);
// Finally, adapt the outside events to our kind of events as per spec
adaptEvents(m_admin);
@@ -356,7 +326,7 @@
}
else
{
- m_admin.update(handlerTasks, m_timeout, m_ignoreTimeout);
+ m_admin.update(m_timeout, m_ignoreTimeout, m_requireTopic);
}
}
@@ -426,10 +396,10 @@
try
{
return new MetaTypeProviderImpl((ManagedService)managedService,
- m_cacheSize, m_threadPoolSize, m_timeout, m_requireTopic,
+ m_threadPoolSize, m_timeout, m_requireTopic,
m_ignoreTimeout);
}
- catch (Throwable t)
+ catch (final Throwable t)
{
// we simply ignore this
}
diff --git a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/EventAdminImpl.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/EventAdminImpl.java
deleted file mode 100644
index 9c868b1..0000000
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/EventAdminImpl.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * 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.List;
-
-import org.apache.felix.eventadmin.impl.dispatch.DefaultThreadPool;
-import org.apache.felix.eventadmin.impl.handler.HandlerTasks;
-import org.apache.felix.eventadmin.impl.tasks.*;
-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:dev@felix.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 DeliverTask m_postManager;
-
- // The synchronous event dispatcher
- private final SyncDeliverTasks 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 syncPool The synchronous thread pool
- * @param asyncPool The asynchronous thread pool
- */
- public EventAdminImpl(final HandlerTasks managers,
- final DefaultThreadPool syncPool,
- final DefaultThreadPool asyncPool,
- final int timeout,
- final String[] ignoreTimeout)
- {
- checkNull(managers, "Managers");
- checkNull(syncPool, "syncPool");
- checkNull(asyncPool, "asyncPool");
-
- m_managers = managers;
-
- m_sendManager = new SyncDeliverTasks(syncPool,
- (timeout > 100 ? timeout : 0),
- ignoreTimeout);
-
- m_postManager = new AsyncDeliverTasks(asyncPool, m_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 List createHandlerTasks(final Event event)
- {
- throw new IllegalStateException("The EventAdmin is stopped");
- }
- };
- }
-
- /**
- * Update the event admin with new configuration.
- */
- public void update(final HandlerTasks managers, final int timeout,
- final String[] ignoreTimeout)
- {
- m_managers = managers;
- m_sendManager.update(timeout, ignoreTimeout);
- }
-
- /**
- * 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(List managers,
- final DeliverTask manager)
- {
- if (managers != null && managers.size() > 0 )
- {
- // 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.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/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 0ccc102..c5dbf72 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
@@ -28,11 +28,12 @@
/**
* The optional meta type provider for the event admin config.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
public class MetaTypeProviderImpl
implements MetaTypeProvider, ManagedService
{
- private final int m_cacheSize;
private final int m_threadPoolSize;
private final int m_timeout;
private final boolean m_requireTopic;
@@ -41,11 +42,10 @@
private final ManagedService m_delegatee;
public MetaTypeProviderImpl(final ManagedService delegatee,
- final int cacheSize, final int threadPoolSize,
+ final int threadPoolSize,
final int timeout, final boolean requireTopic,
final String[] ignoreTimeout)
{
- m_cacheSize = cacheSize;
m_threadPoolSize = threadPoolSize;
m_timeout = timeout;
m_requireTopic = requireTopic;
@@ -85,11 +85,6 @@
{
final ArrayList adList = new ArrayList();
- adList.add( new AttributeDefinitionImpl( Configuration.PROP_CACHE_SIZE, "Cache Size",
- "The size of various internal caches. The default value is 30. Increase in case " +
- "of a large number (more then 100) of services. A value less then 10 triggers the " +
- "default value.", m_cacheSize) );
-
adList.add( new AttributeDefinitionImpl( Configuration.PROP_THREAD_POOL_SIZE, "Thread Pool Size",
"The size of the thread pool. The default value is 10. Increase in case of a large amount " +
"of synchronous events where the event handler services in turn send new synchronous events in " +
diff --git a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/adapter/AbstractAdapter.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/adapter/AbstractAdapter.java
index 8e5cde2..8a687d4 100644
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/adapter/AbstractAdapter.java
+++ b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/adapter/AbstractAdapter.java
@@ -24,6 +24,7 @@
/**
* Abstract base class for all adapters.
* This class allows to exchange the event admin at runtime
+ *
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
public abstract class AbstractAdapter
diff --git a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/BlackList.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/BlackList.java
deleted file mode 100644
index c409487..0000000
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/BlackList.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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 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:dev@felix.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/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/BlacklistingHandlerTasks.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/BlacklistingHandlerTasks.java
deleted file mode 100644
index 84282ed..0000000
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/BlacklistingHandlerTasks.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * 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.util.ArrayList;
-import java.util.List;
-
-import org.apache.felix.eventadmin.impl.security.PermissionsUtil;
-import org.apache.felix.eventadmin.impl.tasks.HandlerTaskImpl;
-import org.apache.felix.eventadmin.impl.util.LogWrapper;
-import org.osgi.framework.*;
-import org.osgi.service.event.*;
-
-/**
- * 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:dev@felix.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;
-
- /**
- * 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
- */
- public BlacklistingHandlerTasks(final BundleContext context,
- final BlackList blackList,
- final TopicHandlerFilters topicHandlerFilters, final Filters filters)
- {
- checkNull(context, "Context");
- checkNull(blackList, "BlackList");
- checkNull(topicHandlerFilters, "TopicHandlerFilters");
- checkNull(filters, "Filters");
-
- m_context = context;
-
- m_blackList = blackList;
-
- m_topicHandlerFilters = topicHandlerFilters;
-
- m_filters = filters;
- }
-
- /**
- * 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 List createHandlerTasks(final Event event)
- {
- ServiceReference[] handlerRefs = null;
-
- 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.length == 0 )
- {
- return null;
- }
-
- final List result = new ArrayList();
- for (int i = 0; i < handlerRefs.length; i++)
- {
- final ServiceReference ref = handlerRefs[i];
- final Bundle serviceBundle = ref.getBundle();
- if ( serviceBundle != null )
- {
- if (!m_blackList.contains(ref)
- && serviceBundle.hasPermission(
- PermissionsUtil.createSubscribePermission(event.getTopic())))
- {
- try
- {
- if (event.matches(m_filters.createFilter(
- (String) ref.getProperty(EventConstants.EVENT_FILTER))))
- {
- result.add(new HandlerTaskImpl(ref, event, this));
- }
- } catch (InvalidSyntaxException e)
- {
- LogWrapper.getLogger().log(
- ref,
- LogWrapper.LOG_WARNING,
- "Invalid EVENT_FILTER - Blacklisting ServiceReference ["
- + ref + " | Bundle("
- + serviceBundle + ")]", e);
-
- m_blackList.add(ref);
- }
- }
- }
- }
-
- return result;
- }
-
- /**
- * 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);
- }
-
- /**
- * Unget the service reference for the given event handler unless it is the
- * NullEventHandler. This is a private method and only public due to
- * its usage in a friend class.
- *
- * @param handler The event handler service to unget
- * @param handlerRef The service reference to unget
- */
- public void ungetEventHandler(final EventHandler handler,
- final ServiceReference handlerRef)
- {
- if(m_nullEventHandler != handler)
- {
- // Is the handler not unregistered or blacklisted?
- if(!m_blackList.contains(handlerRef) && (null !=
- handlerRef.getBundle()))
- {
- m_context.ungetService(handlerRef);
- }
- }
- }
-
- /*
- * 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/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/CacheFilters.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/CacheFilters.java
deleted file mode 100644
index 8f04cb4..0000000
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/CacheFilters.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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 org.apache.felix.eventadmin.impl.util.CacheMap;
-import org.osgi.framework.*;
-
-/**
- * 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:dev@felix.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 TRUE_FILTER in case
- * the string is <tt>null</tt>.
- *
- * @param filter The filter as a string
- * @return The <tt>Filter</tt> of the filter string or the TRUE_FILTER 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)
- */
- public Filter createFilter(String filter)
- throws InvalidSyntaxException
- {
- Filter result = (Filter) ((null != filter) ? m_cache.get(filter)
- : TRUE_FILTER);
-
- if (null == result)
- {
- result = m_context.createFilter(filter);
-
- m_cache.add(filter, result);
- }
-
- return result;
- }
-
-}
diff --git a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/CacheTopicHandlerFilters.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/CacheTopicHandlerFilters.java
deleted file mode 100644
index 3047f75..0000000
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/CacheTopicHandlerFilters.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * 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 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:dev@felix.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/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/CleanBlackList.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/CleanBlackList.java
deleted file mode 100644
index 670edb2..0000000
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/CleanBlackList.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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.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:dev@felix.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/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
new file mode 100644
index 0000000..c714afc
--- /dev/null
+++ b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/EventAdminImpl.java
@@ -0,0 +1,149 @@
+/*
+ * 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 org.apache.felix.eventadmin.impl.tasks.*;
+import org.osgi.framework.BundleContext;
+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: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;
+
+ /**
+ * 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)
+ {
+ checkNull(syncPool, "syncPool");
+ checkNull(asyncPool, "asyncPool");
+
+ 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), 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), event);
+ }
+
+ /**
+ * 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)
+ {
+ 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");
+ }
+ }
+}
diff --git a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/EventHandlerProxy.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/EventHandlerProxy.java
new file mode 100644
index 0000000..7216f40
--- /dev/null
+++ b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/EventHandlerProxy.java
@@ -0,0 +1,342 @@
+/*
+ * 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 org.apache.felix.eventadmin.impl.security.PermissionsUtil;
+import org.apache.felix.eventadmin.impl.util.LogWrapper;
+import org.osgi.framework.*;
+import org.osgi.service.event.*;
+
+/**
+ * This is a proxy for event handlers. It gets the real event handler
+ * on demand and prepares some information for faster processing.
+ *
+ * It checks the timeout handling for the implementation as well as
+ * blacklisting the handler.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class EventHandlerProxy {
+
+ /** The service reference for the event handler. */
+ private final ServiceReference reference;
+
+ /** The handler context. */
+ private final EventHandlerTracker.HandlerContext handlerContext;
+
+ /** The event topics. */
+ private volatile String[] topics;
+
+ /** Optional filter. */
+ private volatile Filter filter;
+
+ /** Lazy fetched event handler. */
+ private volatile EventHandler handler;
+
+ /** Is this handler blacklisted? */
+ private volatile boolean blacklisted;
+
+ /** Use timeout. */
+ private boolean useTimeout;
+
+ /**
+ * Create an EventHandlerProxy.
+ *
+ * @param context The handler context
+ * @param reference Reference to the EventHandler
+ */
+ public EventHandlerProxy(final EventHandlerTracker.HandlerContext context,
+ final ServiceReference reference)
+ {
+ this.handlerContext = context;
+ this.reference = reference;
+ }
+
+ /**
+ * Update the state with current properties from the service
+ * @return <code>true</code> if the handler configuration is valid.
+ */
+ public boolean update()
+ {
+ this.blacklisted = false;
+ boolean valid = true;
+ // First check, topic
+ final Object topicObj = reference.getProperty(EventConstants.EVENT_TOPIC);
+ if (topicObj instanceof String)
+ {
+ if ( topicObj.toString().equals("*") )
+ {
+ this.topics = null;
+ }
+ else
+ {
+ this.topics = new String[] {topicObj.toString()};
+ }
+ }
+ else if (topicObj instanceof String[])
+ {
+ // check if one value matches '*'
+ final String[] values = (String[])topicObj;
+ boolean matchAll = false;
+ for(int i=0;i<values.length;i++)
+ {
+ if ( "*".equals(values[i]) )
+ {
+ matchAll = true;
+ }
+ }
+ if ( matchAll )
+ {
+ this.topics = null;
+ }
+ else
+ {
+ this.topics = values;
+ }
+ }
+ else if ( topicObj == null && !this.handlerContext.requireTopic )
+ {
+ this.topics = null;
+ }
+ else
+ {
+ final String reason;
+ if ( topicObj == null )
+ {
+ reason = "Missing";
+ }
+ else
+ {
+ reason = "Neither of type String nor String[] : " + topicObj.getClass().getName();
+ }
+ LogWrapper.getLogger().log(
+ this.reference,
+ LogWrapper.LOG_WARNING,
+ "Invalid EVENT_TOPICS : " + reason + " - Ignoring ServiceReference ["
+ + this.reference + " | Bundle("
+ + this.reference.getBundle() + ")]");
+ this.topics = null;
+ valid = false;
+ }
+ // Second check filter (but only if topics is valid)
+ Filter handlerFilter = null;
+ if ( this.topics != null )
+ {
+ final Object filterObj = reference.getProperty(EventConstants.EVENT_FILTER);
+ if (filterObj instanceof String)
+ {
+ try
+ {
+ handlerFilter = this.handlerContext.bundleContext.createFilter(filterObj.toString());
+ }
+ catch (final InvalidSyntaxException e)
+ {
+ valid = false;
+ LogWrapper.getLogger().log(
+ this.reference,
+ LogWrapper.LOG_WARNING,
+ "Invalid EVENT_FILTER - Ignoring ServiceReference ["
+ + this.reference + " | Bundle("
+ + this.reference.getBundle() + ")]", e);
+ }
+ }
+ else if ( filterObj != null )
+ {
+ valid = false;
+ LogWrapper.getLogger().log(
+ this.reference,
+ LogWrapper.LOG_WARNING,
+ "Invalid EVENT_FILTER - Ignoring ServiceReference ["
+ + this.reference + " | Bundle("
+ + this.reference.getBundle() + ")]");
+ }
+ }
+ this.filter = handlerFilter;
+
+ // make sure to release the handler
+ this.release();
+
+ return valid;
+ }
+
+ /**
+ * Dispose the proxy and release the handler
+ */
+ public void dispose()
+ {
+ this.release();
+ }
+
+ /**
+ * Get the event handler.
+ */
+ private synchronized EventHandler obtain() {
+ if (this.handler == null)
+ {
+ try
+ {
+ this.handler = (EventHandler)this.handlerContext.bundleContext.getService(this.reference);
+ if ( this.handler != null )
+ {
+ this.checkTimeout(this.handler.getClass().getName());
+ }
+ }
+ catch (final IllegalStateException ignore)
+ {
+ // event handler might be stopped - ignore
+ }
+ }
+ return this.handler;
+ }
+
+ /**
+ * Release the handler
+ */
+ private synchronized void release()
+ {
+ if ( this.handler != null )
+ {
+ try
+ {
+ this.handlerContext.bundleContext.ungetService(this.reference);
+ }
+ catch (final IllegalStateException ignore)
+ {
+ // event handler might be stopped - ignore
+ }
+ this.handler = null;
+ }
+ }
+
+ /**
+ * Get the topics of this handler.
+ * If this handler matches all topics <code>null</code> is returned
+ */
+ public String[] getTopics()
+ {
+ return this.topics;
+ }
+
+ /**
+ * Check if this handler is allowed to receive the event
+ * - blacklisted
+ * - check filter
+ * - check permission
+ */
+ public boolean canDeliver(final Event event)
+ {
+ if ( this.blacklisted )
+ {
+ return false;
+ }
+ final Bundle bundle = reference.getBundle();
+ // is service unregistered?
+ if (bundle == null)
+ {
+ return false;
+ }
+
+ // filter match
+ final Filter eventFilter = this.filter;
+ if ( eventFilter != null && !event.matches(eventFilter) )
+ {
+ return false;
+ }
+
+ // permission check
+ final Object p = PermissionsUtil.createSubscribePermission(event.getTopic());
+ if (p != null && !bundle.hasPermission(p) )
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Should a timeout be used for this handler?
+ */
+ public boolean useTimeout()
+ {
+ return this.useTimeout;
+ }
+
+ /**
+ * Check the timeout configuration for this handler.
+ */
+ private void checkTimeout(final String className)
+ {
+ if ( this.handlerContext.ignoreTimeoutMatcher != null )
+ {
+ for(int i=0;i<this.handlerContext.ignoreTimeoutMatcher.length;i++)
+ {
+ if ( this.handlerContext.ignoreTimeoutMatcher[i] != null)
+ {
+ if ( this.handlerContext.ignoreTimeoutMatcher[i].match(className) )
+ {
+ this.useTimeout = false;
+ return;
+ }
+ }
+ }
+ }
+ this.useTimeout = true;
+ }
+
+ /**
+ * Send the event.
+ */
+ public void sendEvent(final Event event)
+ {
+ final EventHandler handlerService = this.obtain();
+ if (handlerService == null)
+ {
+ return;
+ }
+
+ try
+ {
+ handlerService.handleEvent(event);
+ }
+ catch (final Throwable e)
+ {
+ // The spec says that we must catch exceptions and log them:
+ LogWrapper.getLogger().log(
+ this.reference,
+ LogWrapper.LOG_WARNING,
+ "Exception during event dispatch [" + event + " | "
+ + this.reference + " | Bundle("
+ + this.reference.getBundle() + ")]", e);
+ }
+ }
+
+ /**
+ * Blacklist the handler.
+ */
+ public void blackListHandler()
+ {
+ LogWrapper.getLogger().log(
+ LogWrapper.LOG_WARNING,
+ "Blacklisting ServiceReference [" + this.reference + " | Bundle("
+ + this.reference.getBundle() + ")] due to timeout!");
+ this.blacklisted = true;
+ // we can free the handler now.
+ this.release();
+ }
+}
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
new file mode 100644
index 0000000..5babd1a
--- /dev/null
+++ b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/EventHandlerTracker.java
@@ -0,0 +1,396 @@
+/*
+ * 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.util.*;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventHandler;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * The event handler tracker keeps track of all event handler services.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class EventHandlerTracker extends ServiceTracker {
+
+ /** The proxies in this list match all events. */
+ private volatile List matchingAllEvents;
+
+ /** This is a map for exact topic matches. The key is the topic,
+ * the value is a list of proxies.
+ */
+ private volatile Map matchingTopic;
+
+ /** This is a map for wildcard topics. The key is the prefix of the topic,
+ * the value is a list of proxies
+ */
+ private volatile Map matchingPrefixTopic;
+
+
+ /** The context for the proxies. */
+ private HandlerContext handlerContext;
+
+ public EventHandlerTracker(final BundleContext context) {
+ super(context, EventHandler.class.getName(), null);
+
+ // we start with empty collections
+ this.matchingAllEvents = new ArrayList();
+ this.matchingTopic = new HashMap();
+ this.matchingPrefixTopic = new HashMap();
+ }
+
+ /**
+ * Update the timeout configuration.
+ * @param ignoreTimeout
+ */
+ public void update(final String[] ignoreTimeout, final boolean requireTopic) {
+ final Matcher[] ignoreTimeoutMatcher;
+ if ( ignoreTimeout == null || ignoreTimeout.length == 0 )
+ {
+ ignoreTimeoutMatcher = null;
+ }
+ else
+ {
+ 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(".") )
+ {
+ ignoreTimeoutMatcher[i] = new PackageMatcher(value.substring(0, value.length() - 1));
+ }
+ else if ( value.endsWith("*") )
+ {
+ ignoreTimeoutMatcher[i] = new SubPackageMatcher(value.substring(0, value.length() - 1));
+ }
+ else
+ {
+ ignoreTimeoutMatcher[i] = new ClassMatcher(value);
+ }
+ }
+ }
+ }
+ this.handlerContext = new HandlerContext(this.context, ignoreTimeoutMatcher, requireTopic);
+ }
+
+ /**
+ * @see org.osgi.util.tracker.ServiceTracker#addingService(org.osgi.framework.ServiceReference)
+ */
+ public Object addingService(final ServiceReference reference) {
+ final EventHandlerProxy proxy = new EventHandlerProxy(this.handlerContext, reference);
+ if ( proxy.update() ) {
+ this.put(proxy);
+ }
+ return proxy;
+ }
+
+ /**
+ * @see org.osgi.util.tracker.ServiceTracker#modifiedService(org.osgi.framework.ServiceReference, java.lang.Object)
+ */
+ public void modifiedService(final ServiceReference reference, final Object service) {
+ final EventHandlerProxy proxy = (EventHandlerProxy)service;
+ this.remove(proxy);
+ if ( proxy.update() ) {
+ this.put(proxy);
+ }
+ }
+
+ /**
+ * @see org.osgi.util.tracker.ServiceTracker#removedService(org.osgi.framework.ServiceReference, java.lang.Object)
+ */
+ public void removedService(ServiceReference reference, Object service) {
+ final EventHandlerProxy proxy = (EventHandlerProxy)service;
+ this.remove(proxy);
+ proxy.dispose();
+ }
+
+ private void updateMap(final Map proxyListMap, final String key, final EventHandlerProxy proxy, final boolean add) {
+ List proxies = (List)proxyListMap.get(key);
+ if (proxies == null) {
+ if ( !add )
+ {
+ return;
+ }
+ proxies = new ArrayList();
+ } else {
+ proxies = new ArrayList(proxies);
+ }
+ if ( add )
+ {
+ proxies.add(proxy);
+ }
+ else
+ {
+ proxies.remove(proxy);
+ }
+ if ( proxies.size() == 0 )
+ {
+ proxyListMap.remove(key);
+ }
+ else
+ {
+ proxyListMap.put(key, proxies);
+ }
+ }
+
+ /**
+ * Check the topics of the event handler and put it into the
+ * corresponding collections.
+ * We always create new collections - while this is "expensive"
+ * it allows us to read from them unsynced
+ */
+ private synchronized void put(final EventHandlerProxy proxy) {
+ final String[] topics = proxy.getTopics();
+ if ( topics == null )
+ {
+ final List newMatchingAllEvents = new ArrayList(this.matchingAllEvents);
+ newMatchingAllEvents.add(proxy);
+ this.matchingAllEvents = newMatchingAllEvents;
+ }
+ else
+ {
+ Map newMatchingTopic = null;
+ Map newMatchingPrefixTopic = null;
+ for(int i = 0; i < topics.length; i++) {
+ final String topic = topics[i];
+
+ if ( topic.endsWith("/*") )
+ {
+ // prefix topic: we remove the /*
+ if ( newMatchingPrefixTopic == null )
+ {
+ newMatchingPrefixTopic = new HashMap(this.matchingPrefixTopic);
+ }
+
+ final String prefix = topic.substring(0, topic.length() - 2);
+ this.updateMap(newMatchingPrefixTopic, prefix, proxy, true);
+ }
+ else
+ {
+ // exact match
+ if ( newMatchingTopic == null )
+ {
+ newMatchingTopic = new HashMap(this.matchingTopic);
+ }
+
+ this.updateMap(newMatchingTopic, topic, proxy, true);
+ }
+ }
+ if ( newMatchingTopic != null )
+ {
+ this.matchingTopic = newMatchingTopic;
+ }
+ if ( newMatchingPrefixTopic != null )
+ {
+ this.matchingPrefixTopic = newMatchingPrefixTopic;
+ }
+ }
+ }
+
+ /**
+ * Check the topics of the event handler and remove it from the
+ * corresponding collections.
+ * We always create new collections - while this is "expensive"
+ * it allows us to read from them unsynced
+ */
+ private synchronized void remove(final EventHandlerProxy proxy) {
+ final String[] topics = proxy.getTopics();
+ if ( topics == null )
+ {
+ final List newMatchingAllEvents = new ArrayList(this.matchingAllEvents);
+ newMatchingAllEvents.remove(proxy);
+ this.matchingAllEvents = newMatchingAllEvents;
+ } else {
+ Map newMatchingTopic = null;
+ Map newMatchingPrefixTopic = null;
+ for(int i = 0; i < topics.length; i++) {
+ final String topic = topics[i];
+
+ if ( topic.endsWith("/*") )
+ {
+ // prefix topic: we remove the /*
+ if ( newMatchingPrefixTopic == null )
+ {
+ newMatchingPrefixTopic = new HashMap(this.matchingPrefixTopic);
+ }
+
+ final String prefix = topic.substring(0, topic.length() - 2);
+ this.updateMap(newMatchingPrefixTopic, prefix, proxy, false);
+ }
+ else
+ {
+ // exact match
+ if ( newMatchingTopic == null )
+ {
+ newMatchingTopic = new HashMap(this.matchingTopic);
+ }
+
+ this.updateMap(newMatchingTopic, topic, proxy, false);
+ }
+ }
+ if ( newMatchingTopic != null )
+ {
+ this.matchingTopic = newMatchingTopic;
+ }
+ if ( newMatchingPrefixTopic != null )
+ {
+ this.matchingPrefixTopic = newMatchingPrefixTopic;
+ }
+ }
+ }
+
+ /**
+ * Get all handlers for this event
+ *
+ * @param event The event topic
+ * @return All handlers for the event
+ */
+ public Collection getHandlers(final Event event) {
+ final String topic = event.getTopic();
+
+ final Set handlers = new HashSet();
+
+ // Add all handlers matching everything
+ handlers.addAll(this.matchingAllEvents);
+
+ // Now check for prefix matches
+ if ( !this.matchingPrefixTopic.isEmpty() )
+ {
+ int pos = topic.lastIndexOf('/');
+ while (pos != -1)
+ {
+ final String prefix = topic.substring(0, pos);
+ List proxies = (List)this.matchingPrefixTopic.get(prefix);
+ if (proxies != null)
+ {
+ handlers.addAll(proxies);
+ }
+
+ pos = prefix.lastIndexOf('/');
+ }
+ }
+
+ // Add the handlers for matching topic names
+ List proxies = (List)this.matchingTopic.get(topic);
+ if (proxies != null) {
+ handlers.addAll(proxies);
+ }
+
+ // now check permission and filters
+ final Iterator i = handlers.iterator();
+ while ( i.hasNext() ) {
+ final EventHandlerProxy proxy = (EventHandlerProxy) i.next();
+ if ( !proxy.canDeliver(event) ) {
+ i.remove();
+ }
+ }
+ return handlers;
+ }
+
+ /**
+ * The matcher interface for checking if timeout handling
+ * is disabled for the handler.
+ * Matching is based on the class name of the event handler.
+ */
+ static interface Matcher
+ {
+ boolean match(String className);
+ }
+
+ /** Match a package. */
+ 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);
+ }
+ }
+
+ /** Match a package or sub package. */
+ 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);
+ }
+ }
+
+ /** Match a class name. */
+ 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 context object passed to the proxies.
+ */
+ static final class HandlerContext
+ {
+ /** The bundle context. */
+ public final BundleContext bundleContext;
+
+ /** The matchers for ignore timeout handling. */
+ public final Matcher[] ignoreTimeoutMatcher;
+
+ /** Is a topic required. */
+ public final boolean requireTopic;
+
+ public HandlerContext(final BundleContext bundleContext,
+ final Matcher[] ignoreTimeoutMatcher,
+ final boolean requireTopic)
+ {
+ this.bundleContext = bundleContext;
+ this.ignoreTimeoutMatcher = ignoreTimeoutMatcher;
+ this.requireTopic = requireTopic;
+ }
+ }
+}
diff --git a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/Filters.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/Filters.java
deleted file mode 100644
index 40ee69d..0000000
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/Filters.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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.util.Dictionary;
-
-import org.osgi.framework.*;
-
-/**
- * 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:dev@felix.apache.org">Felix Project Team</a>
- */
-public interface Filters
-{
- /**
- * A null filter object that matches any given service reference.
- */
- 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;
- }
- };
-
- /**
- * Create a filter for the given filter string or return the TRUE_FILTER in case
- * the string is <tt>null</tt>.
- *
- * @param filter The filter as a string
- * @return The <tt>Filter</tt> of the filter string or the TRUE_FILTER if the
- * filter string was null
- * @throws InvalidSyntaxException if <tt>BundleContext.createFilter()</tt>
- * throws an <tt>InvalidSyntaxException</tt>
- */
- Filter createFilter(final String filter)
- throws InvalidSyntaxException;
-}
diff --git a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/HandlerTasks.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/HandlerTasks.java
deleted file mode 100644
index 8b51af9..0000000
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/HandlerTasks.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.util.List;
-
-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:dev@felix.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
- */
- List createHandlerTasks(final Event event);
-}
diff --git a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/TopicHandlerFilters.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/TopicHandlerFilters.java
deleted file mode 100644
index c55c742..0000000
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/handler/TopicHandlerFilters.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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;
-
-/**
- * The factory for <tt>EventHandler</tt> filters based on a certain topic.
- *
- * @author <a href="mailto:dev@felix.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.
- */
- String createFilterForTopic(final String topic);
-}
diff --git a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/security/EventAdminSecurityDecorator.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/security/EventAdminSecurityDecorator.java
index 537047e..a1c0e19 100644
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/security/EventAdminSecurityDecorator.java
+++ b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/security/EventAdminSecurityDecorator.java
@@ -18,6 +18,8 @@
*/
package org.apache.felix.eventadmin.impl.security;
+import java.security.Permission;
+
import org.osgi.framework.Bundle;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;
@@ -128,7 +130,7 @@
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
@@ -136,7 +138,8 @@
*/
private void checkPermission(final String topic)
{
- if(!m_bundle.hasPermission(PermissionsUtil.createPublishPermission(topic)))
+ final Permission p = PermissionsUtil.createPublishPermission(topic);
+ if(p != null && !m_bundle.hasPermission(p))
{
throw new SecurityException("Bundle[" + m_bundle +
"] has no PUBLISH permission for topic [" + topic + "]");
diff --git a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/security/PermissionsUtil.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/security/PermissionsUtil.java
index 240d363..3bcccd7 100644
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/security/PermissionsUtil.java
+++ b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/security/PermissionsUtil.java
@@ -18,6 +18,8 @@
*/
package org.apache.felix.eventadmin.impl.security;
+import java.security.Permission;
+
import org.osgi.service.event.TopicPermission;
/**
@@ -27,74 +29,61 @@
*/
public abstract class PermissionsUtil
{
+ /** Marker if permission created failed. */
+ private static volatile boolean createPermissions = true;
+
/**
* Creates a <tt>TopicPermission</tt> for the given topic and the type PUBLISH
- * 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.
+ * @return The created permission or <tt>null</tt> in case the
+ * permission could not be created.
*
* @see org.osgi.service.event.TopicPermission
*/
- public static Object createPublishPermission(final String topic)
+ public static Permission createPublishPermission(final String topic)
{
- Object result;
- try
+ if ( createPermissions )
{
- result = new org.osgi.service.event.TopicPermission(topic, TopicPermission.PUBLISH);
- } 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();
+ try
+ {
+ return new org.osgi.service.event.TopicPermission(topic, TopicPermission.PUBLISH);
+ }
+ catch (Throwable t)
+ {
+ // This might happen in case security is not supported
+ createPermissions = false;
+ }
}
- return result;
+ return null;
}
/**
* Creates a <tt>TopicPermission</tt> for the given topic and the type 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
+ * @return The created permission or a <tt>null</tt> in case the
* permission could not be created.
*
* @see org.osgi.service.event.TopicPermission
*/
- public static Object createSubscribePermission(final String topic)
+ public static Permission createSubscribePermission(final String topic)
{
- Object result;
- try
+ if ( createPermissions )
{
- result = new org.osgi.service.event.TopicPermission(topic, TopicPermission.SUBSCRIBE);
- } 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();
+ try
+ {
+ return new org.osgi.service.event.TopicPermission(topic, TopicPermission.SUBSCRIBE);
+ }
+ catch (Throwable t)
+ {
+ // This might happen in case security is not supported
+ createPermissions = false;
+ }
}
- return result;
+ return null;
}
-
}
diff --git a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/tasks/AsyncDeliverTasks.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/tasks/AsyncDeliverTasks.java
index 19a1e13..e3653f8 100644
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/tasks/AsyncDeliverTasks.java
+++ b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/tasks/AsyncDeliverTasks.java
@@ -20,14 +20,14 @@
import java.util.*;
-import org.apache.felix.eventadmin.impl.dispatch.DefaultThreadPool;
+import org.osgi.service.event.Event;
/**
* This class does the actual work of the asynchronous event dispatch.
*
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
-public class AsyncDeliverTasks implements DeliverTask
+public class AsyncDeliverTasks
{
/** The thread pool to use to spin-off new threads. */
private final DefaultThreadPool m_pool;
@@ -36,7 +36,7 @@
* is the sync deliver tasks as this has all the code for timeout
* handling etc.
*/
- private final DeliverTask m_deliver_task;
+ private final SyncDeliverTasks m_deliver_task;
/** A map of running threads currently delivering async events. */
private final Map m_running_threads = new HashMap();
@@ -49,7 +49,7 @@
* dispatching thread is used to send a synchronous event
* @param deliverTask The deliver tasks for dispatching the event.
*/
- public AsyncDeliverTasks(final DefaultThreadPool pool, final DeliverTask deliverTask)
+ public AsyncDeliverTasks(final DefaultThreadPool pool, final SyncDeliverTasks deliverTask)
{
m_pool = pool;
m_deliver_task = deliverTask;
@@ -60,9 +60,8 @@
*
* @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 List tasks)
+ public void execute(final Collection tasks, final Event event)
{
final Thread currentThread = Thread.currentThread();
TaskExecuter executer = null;
@@ -71,11 +70,11 @@
TaskExecuter runningExecutor = (TaskExecuter)m_running_threads.get(currentThread);
if ( runningExecutor != null )
{
- runningExecutor.add(tasks);
+ runningExecutor.add(tasks, event);
}
else
{
- executer = new TaskExecuter( tasks, currentThread );
+ executer = new TaskExecuter( tasks, event, currentThread );
m_running_threads.put(currentThread, executer);
}
}
@@ -91,10 +90,10 @@
private final Object m_key;
- public TaskExecuter(final List tasks, final Object key)
+ public TaskExecuter(final Collection tasks, final Event event, final Object key)
{
m_key = key;
- m_tasks.add(tasks);
+ m_tasks.add(new Object[] {tasks, event});
}
public void run()
@@ -102,12 +101,12 @@
boolean running;
do
{
- List tasks = null;
+ Object[] tasks = null;
synchronized ( m_tasks )
{
- tasks = (List) m_tasks.remove(0);
+ tasks = (Object[]) m_tasks.remove(0);
}
- m_deliver_task.execute(tasks);
+ m_deliver_task.execute((Collection)tasks[0], (Event)tasks[1]);
synchronized ( m_running_threads )
{
running = m_tasks.size() > 0;
@@ -119,11 +118,11 @@
} while ( running );
}
- public void add(final List tasks)
+ public void add(final Collection tasks, final Event event)
{
synchronized ( m_tasks )
{
- m_tasks.add(tasks);
+ m_tasks.add(new Object[] {tasks, event});
}
}
}
diff --git a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/dispatch/DefaultThreadPool.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/tasks/DefaultThreadPool.java
similarity index 94%
rename from eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/dispatch/DefaultThreadPool.java
rename to eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/tasks/DefaultThreadPool.java
index 7e7ed55..7facee1 100644
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/dispatch/DefaultThreadPool.java
+++ b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/tasks/DefaultThreadPool.java
@@ -14,9 +14,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.felix.eventadmin.impl.dispatch;
+package org.apache.felix.eventadmin.impl.tasks;
-import org.apache.felix.eventadmin.impl.tasks.SyncThread;
import org.apache.felix.eventadmin.impl.util.LogWrapper;
import EDU.oswego.cs.dl.util.concurrent.*;
@@ -95,13 +94,13 @@
* Execute the task in a free thread or create a new one.
* @param task The task to execute
*/
- public void executeTask(Runnable task)
+ public void executeTask(final Runnable task)
{
try
{
super.execute(task);
}
- catch (Throwable t)
+ catch (final Throwable t)
{
LogWrapper.getLogger().log(
LogWrapper.LOG_WARNING,
diff --git a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/tasks/DeliverTask.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/tasks/DeliverTask.java
deleted file mode 100644
index 216a8b2..0000000
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/tasks/DeliverTask.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.tasks;
-
-import java.util.List;
-
-/**
- * Dispatch given event dispatch tasks.
- *
- * @author <a href="mailto:dev@felix.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(List handlerTasks);
-}
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
deleted file mode 100644
index 613d74d..0000000
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/tasks/HandlerTask.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.tasks;
-
-/**
- * A task that will deliver its event to its <tt>EventHandler</tt> when executed
- * or blacklist the handler, respectively.
- *
- * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
- */
-public interface HandlerTask
-{
- /**
- * Return the class name of the handler
- */
- String getHandlerClassName();
-
- /**
- * Deliver the event to the handler.
- */
- void execute();
-
- /**
- * Blacklist the handler.
- */
- void blackListHandler();
-}
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
deleted file mode 100644
index e5b3c03..0000000
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/tasks/HandlerTaskImpl.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * 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.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:dev@felix.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#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()
- {
- // Get the service object
- final EventHandler handler = m_handlerTasks
- .getEventHandler(m_eventHandlerRef);
-
- try
- {
- handler.handleEvent(m_event);
- }
- catch (final Throwable 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);
- }
- finally
- {
- m_handlerTasks.ungetEventHandler(handler, m_eventHandlerRef);
- }
- }
-
- /**
- * @see org.apache.felix.eventadmin.impl.tasks.HandlerTask#blackListHandler()
- */
- public void blackListHandler()
- {
- m_handlerTasks.blackList(m_eventHandlerRef);
- }
-}
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 875accc..a06f2c8 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
@@ -18,10 +18,11 @@
*/
package org.apache.felix.eventadmin.impl.tasks;
+import java.util.Collection;
import java.util.Iterator;
-import java.util.List;
-import org.apache.felix.eventadmin.impl.dispatch.DefaultThreadPool;
+import org.apache.felix.eventadmin.impl.handler.EventHandlerProxy;
+import org.osgi.service.event.Event;
import EDU.oswego.cs.dl.util.concurrent.TimeoutException;
@@ -52,119 +53,30 @@
*
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
-public class SyncDeliverTasks implements DeliverTask
+public class SyncDeliverTasks
{
+
/** The thread pool used to spin-off new threads. */
- final DefaultThreadPool m_pool;
+ private final DefaultThreadPool pool;
- /** The timeout for event handlers, 0 = disabled. */
- long m_timeout;
-
- /**
- * The matcher interface for checking if timeout handling
- * is disabled for the handler.
- * Matching is based on the class name of the event handler.
- */
- private static interface Matcher
- {
- boolean match(String className);
- }
-
- /** Match a package. */
- 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);
- }
- }
-
- /** Match a package or sub package. */
- 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);
- }
- }
-
- /** Match a class name. */
- 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 Matcher[] m_ignoreTimeoutMatcher;
+ private long timeout;
/**
* 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 DefaultThreadPool pool, final long timeout, final String[] ignoreTimeout)
+ public SyncDeliverTasks(final DefaultThreadPool pool, final long timeout)
{
- m_pool = pool;
- update(timeout, ignoreTimeout);
+ this.pool = pool;
+ this.update(timeout);
}
- public void update(final long timeout, final String[] ignoreTimeout) {
- m_timeout = timeout;
- if ( ignoreTimeout == null || ignoreTimeout.length == 0 )
- {
- m_ignoreTimeoutMatcher = null;
- }
- else
- {
- Matcher[] 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(".") )
- {
- ignoreTimeoutMatcher[i] = new PackageMatcher(value.substring(0, value.length() - 1));
- }
- else if ( value.endsWith("*") )
- {
- ignoreTimeoutMatcher[i] = new SubPackageMatcher(value.substring(0, value.length() - 1));
- }
- else
- {
- ignoreTimeoutMatcher[i] = new ClassMatcher(value);
- }
- }
- }
- m_ignoreTimeoutMatcher = ignoreTimeoutMatcher;
- }
+ /**
+ * Update the timeout configuration
+ */
+ public void update(final long timeout)
+ {
+ this.timeout = timeout;
}
/**
@@ -172,27 +84,12 @@
* task.
* @param tasks The event handler dispatch tasks to execute
*/
- private boolean useTimeout(final HandlerTask task)
+ private boolean useTimeout(final EventHandlerProxy proxy)
{
- // we only check the classname if a timeout is configured
- if ( m_timeout > 0)
+ // we only check the proxy if a timeout is configured
+ if ( this.timeout > 0)
{
- final Matcher[] ignoreTimeoutMatcher = m_ignoreTimeoutMatcher;
- if ( ignoreTimeoutMatcher != null )
- {
- final String className = task.getHandlerClassName();
- for(int i=0;i<ignoreTimeoutMatcher.length;i++)
- {
- if ( ignoreTimeoutMatcher[i] != null)
- {
- if ( ignoreTimeoutMatcher[i].match(className) )
- {
- return false;
- }
- }
- }
- }
- return true;
+ return proxy.useTimeout();
}
return false;
}
@@ -203,9 +100,8 @@
*
* @param tasks The event handler dispatch tasks to execute
*
- * @see org.apache.felix.eventadmin.impl.tasks.DeliverTask#execute(List)
*/
- public void execute(final List tasks)
+ public void execute(final Collection tasks, final Event event)
{
final Thread sleepingThread = Thread.currentThread();
final SyncThread syncThread = sleepingThread instanceof SyncThread ? (SyncThread)sleepingThread : null;
@@ -213,20 +109,20 @@
final Iterator i = tasks.iterator();
while ( i.hasNext() )
{
- final HandlerTask task = (HandlerTask)i.next();
+ final EventHandlerProxy task = (EventHandlerProxy)i.next();
if ( !useTimeout(task) )
{
// no timeout, we can directly execute
- task.execute();
+ task.sendEvent(event);
}
else if ( syncThread != null )
{
// if this is a cascaded event, we directly use this thread
// otherwise we could end up in a starvation
final long startTime = System.currentTimeMillis();
- task.execute();
- if ( System.currentTimeMillis() - startTime > m_timeout )
+ task.sendEvent(event);
+ if ( System.currentTimeMillis() - startTime > this.timeout )
{
task.blackListHandler();
}
@@ -235,7 +131,7 @@
{
final Rendezvous startBarrier = new Rendezvous();
final Rendezvous timerBarrier = new Rendezvous();
- m_pool.executeTask(new Runnable()
+ this.pool.executeTask(new Runnable()
{
public void run()
{
@@ -244,7 +140,7 @@
// notify the outer thread to start the timer
startBarrier.waitForRendezvous();
// execute the task
- task.execute();
+ task.sendEvent(event);
// stop the timer
timerBarrier.waitForRendezvous();
}
@@ -262,7 +158,7 @@
// if someone wakes us up it's the finished inner task
try
{
- timerBarrier.waitAttemptForRendezvous(m_timeout);
+ timerBarrier.waitAttemptForRendezvous(this.timeout);
}
catch (final TimeoutException ie)
{
diff --git a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/util/CacheMap.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/util/CacheMap.java
deleted file mode 100644
index 13bbbf9..0000000
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/util/CacheMap.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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.util;
-
-/**
- * This is the interface of a simple cache map.
- *
- * @author <a href="mailto:dev@felix.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/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/util/LeastRecentlyUsedCacheMap.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/util/LeastRecentlyUsedCacheMap.java
deleted file mode 100644
index ca69fb3..0000000
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/util/LeastRecentlyUsedCacheMap.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * 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.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:dev@felix.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();
- }
- }
-
-}