blob: 702040455a36a10abde07201954bb007a1f32177 [file] [log] [blame]
Karl Pauls91761932006-04-28 11:12:54 +00001/*
Karl Paulsd7db2462006-06-01 11:49:22 +00002 * Copyright 2006 The Apache Software Foundation
Karl Pauls91761932006-04-28 11:12:54 +00003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17package org.apache.felix.eventadmin.impl;
18
19import org.apache.felix.eventadmin.impl.adapter.BundleEventAdapter;
20import org.apache.felix.eventadmin.impl.adapter.FrameworkEventAdapter;
21import org.apache.felix.eventadmin.impl.adapter.LogEventAdapter;
22import org.apache.felix.eventadmin.impl.adapter.ServiceEventAdapter;
23import org.apache.felix.eventadmin.impl.dispatch.CacheThreadPool;
24import org.apache.felix.eventadmin.impl.dispatch.DelayScheduler;
25import org.apache.felix.eventadmin.impl.dispatch.Scheduler;
26import org.apache.felix.eventadmin.impl.dispatch.TaskHandler;
27import org.apache.felix.eventadmin.impl.dispatch.ThreadPool;
28import org.apache.felix.eventadmin.impl.handler.BlacklistingHandlerTasks;
29import org.apache.felix.eventadmin.impl.handler.CacheFilters;
30import org.apache.felix.eventadmin.impl.handler.CacheTopicHandlerFilters;
31import org.apache.felix.eventadmin.impl.handler.CleanBlackList;
32import org.apache.felix.eventadmin.impl.handler.Filters;
33import org.apache.felix.eventadmin.impl.handler.HandlerTasks;
34import org.apache.felix.eventadmin.impl.handler.TopicHandlerFilters;
35import org.apache.felix.eventadmin.impl.security.CacheTopicPermissions;
36import org.apache.felix.eventadmin.impl.security.SecureEventAdminFactory;
37import org.apache.felix.eventadmin.impl.security.TopicPermissions;
38import org.apache.felix.eventadmin.impl.tasks.AsyncDeliverTasks;
Karl Paulsc8f79602006-05-01 23:01:13 +000039import org.apache.felix.eventadmin.impl.tasks.BlockTask;
Karl Pauls91761932006-04-28 11:12:54 +000040import org.apache.felix.eventadmin.impl.tasks.DeliverTasks;
41import org.apache.felix.eventadmin.impl.tasks.DispatchTask;
42import org.apache.felix.eventadmin.impl.tasks.SyncDeliverTasks;
43import org.apache.felix.eventadmin.impl.util.LeastRecentlyUsedCacheMap;
44import org.apache.felix.eventadmin.impl.util.LogWrapper;
45import org.osgi.framework.BundleActivator;
46import org.osgi.framework.BundleContext;
Karl Paulsc4785ce2006-05-02 22:10:46 +000047import org.osgi.framework.ServiceRegistration;
Karl Pauls91761932006-04-28 11:12:54 +000048import org.osgi.service.event.EventAdmin;
49import org.osgi.service.event.TopicPermission;
50
51/**
52 * The activator of the EventAdmin bundle. This class registers an implementation of
53 * the OSGi R4 <tt>EventAdmin</tt> service (see the Compendium 113) with the
54 * framework. It features timeout-based blacklisting of event-handlers for both,
55 * asynchronous and synchronous event-dispatching (as a spec conform optional
56 * extension).
57 *
58 * The service knows about the following properties which are read at bundle startup:
59 *
60 * <p>
61 * <p>
62 * <tt>org.apache.felix.eventadmin.CacheSize</tt> - The size of various internal
63 * caches.
64 * </p>
65 * The default value is 30. Increase in case of a large number (more then 100) of
66 * <tt>EventHandler</tt> services. A value less then 10 triggers the default value.
67 * </p>
68 * <p>
69 * <p>
70 * <tt>org.apache.felix.eventadmin.ThreadPoolSize</tt> - The size of the thread
71 * pool.
72 * </p>
73 * The default value is 10. Increase in case of a large amount of synchronous events
74 * where the <tt>EventHandler</tt> services in turn send new synchronous events in
75 * the event dispatching thread or a lot of timeouts are to be expected. A value of
76 * less then 2 triggers the default value. A value of 2 effectively disables thread
77 * pooling.
78 * </p>
79 * <p>
80 * <p>
81 * <tt>org.apache.felix.eventadmin.Timeout</tt> - The black-listing timeout in
82 * milliseconds
83 * </p>
84 * The default value is 5000. Increase or decrease at own discretion. A value of less
85 * then 100 turns timeouts off. Any other value is the time in milliseconds granted
86 * to each <tt>EventHandler</tt> before it gets blacklisted.
87 * </p>
88 * <p>
89 * <p>
90 * <tt>org.apache.felix.eventadmin.RequireTopic</tt> - Are <tt>EventHandler</tt>
91 * required to be registered with a topic?
92 * </p>
93 * The default is <tt>true</tt>. The specification says that <tt>EventHandler</tt>
94 * must register with a list of topics they are interested in. Setting this value to
95 * <tt>false</tt> will enable that handlers without a topic are receiving all events
96 * (i.e., they are treated the same as with a topic=*).
97 * </p>
98 *
99 * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
100 */
Karl Pauls91761932006-04-28 11:12:54 +0000101// TODO: Security is in place but untested due to not being implemented by the
102// framework. However, it needs to be revisited once security is implemented.
103// Two places are affected by this namely, security/* and handler/*
104public class Activator implements BundleActivator
105{
106 // The thread pool used - this is a member because we need to close it on stop
107 private volatile ThreadPool m_pool;
108
109 // The asynchronous event queue - this is a member because we need to close it on
110 // stop
111 private volatile TaskHandler m_asyncQueue;
112
113 // The synchronous event queue - this is a member because we need to close it on
114 // stop
115 private volatile TaskHandler m_syncQueue;
116
117 // The actual implementation of the service - this is a member because we need to
118 // close it on stop. Note, security is not part of this implementation but is
119 // added via a decorator in the start method (this is the wrapped object without
120 // the wrapper).
121 private volatile EventAdminImpl m_admin;
122
Karl Paulsc4785ce2006-05-02 22:10:46 +0000123 // The registration of the security decorator factory (i.e., the service)
124 private volatile ServiceRegistration m_registration;
125
Karl Pauls91761932006-04-28 11:12:54 +0000126 /**
127 * Called upon starting of the bundle. Constructs and registers the EventAdmin
128 * service with the framework. Note that the properties of the service are
129 * requested from the context in this method hence, the bundle has to be
130 * restarted in order to take changed properties into account.
131 *
132 * @param context The bundle context passed by the framework
133 *
134 * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
135 */
136 public void start(final BundleContext context)
137 {
138 // init the LogWrapper. Subsequently, the static methods of the LogWrapper
139 // can be used to log messages similar to the LogService. The effect of a
140 // call to any of this methods is either a print to standard out (in case
141 // no LogService is present) or a call to the respective method of
142 // available LogServices (the reason is that this way the bundle is
143 // independent of the org.osgi.service.log package)
144 LogWrapper.setContext(context);
145
146 // The size of various internal caches. At the moment there are 4
147 // internal caches affected. Each will cache the determined amount of
148 // small but frequently used objects (i.e., in case of the default value
149 // we end-up with a total of 120 small objects being cached). A value of less
150 // then 10 triggers the default value.
151 final int cacheSize = getIntProperty("org.apache.felix.eventadmin.CacheSize",
152 context, 30, 10);
153
154 // The size of the internal thread pool. Note that we must execute
155 // each synchronous event dispatch that happens in the synchronous event
156 // dispatching thread in a new thread, hence a small thread pool is o.k.
157 // A value of less then 2 triggers the default value. A value of 2
158 // effectively disables thread pooling. Furthermore, this will be used by
159 // a lazy thread pool (i.e., new threads are created when needed). Ones the
160 // the size is reached and no cached thread is available new threads will
161 // be created.
162 final int threadPoolSize = getIntProperty(
163 "org.apache.felix.eventadmin.ThreadPoolSize", context, 10, 2);
164
165 // The timeout in milliseconds - A value of less then 100 turns timeouts off.
166 // Any other value is the time in milliseconds granted to each EventHandler
167 // before it gets blacklisted.
168 final int timeout = getIntProperty("org.apache.felix.eventadmin.Timeout",
169 context, 5000, Integer.MIN_VALUE);
170
171 // Are EventHandler required to be registered with a topic? - The default is
172 // true. The specification says that EventHandler must register with a list
173 // of topics they are interested in. Setting this value to false will enable
174 // that handlers without a topic are receiving all events
175 // (i.e., they are treated the same as with a topic=*).
176 final boolean requireTopic = getBooleanProperty(
177 "org.apache.felix.eventadmin.RequireTopic", context, true);
178
179 LogWrapper.getLogger().log(LogWrapper.LOG_DEBUG,
180 "org.apache.felix.eventadmin.CacheSize=" + cacheSize);
181
182 LogWrapper.getLogger().log(LogWrapper.LOG_DEBUG,
183 "org.apache.felix.eventadmin.ThreadPoolSize=" + threadPoolSize);
184
185 LogWrapper.getLogger().log(LogWrapper.LOG_DEBUG,
186 "org.apache.felix.eventadmin.Timeout=" + timeout);
187
188 LogWrapper.getLogger().log(LogWrapper.LOG_DEBUG,
189 "org.apache.felix.eventadmin.RequireTopic=" + requireTopic);
190
191 final TopicPermissions publishPermissions = new CacheTopicPermissions(
192 new LeastRecentlyUsedCacheMap(cacheSize), TopicPermission.PUBLISH);
193
194 final TopicPermissions subscribePermissions = new CacheTopicPermissions(
195 new LeastRecentlyUsedCacheMap(cacheSize), TopicPermission.SUBSCRIBE);
196
197 final TopicHandlerFilters topicHandlerFilters =
198 new CacheTopicHandlerFilters(new LeastRecentlyUsedCacheMap(cacheSize),
199 requireTopic);
200
201 final Filters filters = new CacheFilters(
202 new LeastRecentlyUsedCacheMap(cacheSize), context);
203
204 // The handlerTasks object is responsible to determine concerned EventHandler
205 // for a given event. Additionally, it keeps a list of blacklisted handlers.
206 // Note that blacklisting is deactivated by selecting a different scheduler
207 // below (and not in this HandlerTasks object!)
208 final HandlerTasks handlerTasks = new BlacklistingHandlerTasks(context,
209 new CleanBlackList(), topicHandlerFilters, filters,
210 subscribePermissions);
211
212 // Either we need a scheduler that will trigger EventHandler blacklisting
213 // (timeout >= 100) or a null object (timeout < 100)
214 final Scheduler scheduler = createScheduler(timeout);
215
216 // Note that this uses a lazy thread pool that will create new threads on
217 // demand - in case none of its cached threads is free - until threadPoolSize
218 // is reached. Subsequently, a threadPoolSize of 2 effectively disables
219 // caching of threads.
220 m_pool = new CacheThreadPool(threadPoolSize);
221
222 m_asyncQueue = new TaskHandler();
223
224 m_syncQueue = new TaskHandler();
225
226 m_admin = new EventAdminImpl(handlerTasks,
227 createAsyncExecuters(m_asyncQueue, m_syncQueue, scheduler, m_pool),
228 createSyncExecuters(m_syncQueue, scheduler, m_pool));
229
230 // register the admin wrapped in a service factory (SecureEventAdminFactory)
231 // that hands-out the m_admin object wrapped in a decorator that checks
232 // appropriated permissions of each calling bundle
Karl Paulsc4785ce2006-05-02 22:10:46 +0000233 m_registration = context.registerService(EventAdmin.class.getName(),
Karl Pauls91761932006-04-28 11:12:54 +0000234 new SecureEventAdminFactory(m_admin, publishPermissions), null);
235
236 // Finally, adapt the outside events to our kind of events as per spec
237 adaptEvents(context, m_admin);
238 }
Karl Paulsc8f79602006-05-01 23:01:13 +0000239
Karl Pauls91761932006-04-28 11:12:54 +0000240 /**
Karl Paulsc8f79602006-05-01 23:01:13 +0000241 * Called upon stopping the bundle. This will block until all pending events are
242 * delivered. An IllegalStateException will be thrown on new events starting with
243 * the begin of this method. However, it might take some time until we settle
244 * down which is somewhat cumbersome given that the spec asks for return in
Karl Pauls13f116c2006-05-14 15:12:25 +0000245 * a timely manner.
Karl Pauls91761932006-04-28 11:12:54 +0000246 *
247 * @param context The bundle context passed by the framework
248 *
249 * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
250 */
251 public void stop(final BundleContext context)
252 {
Karl Paulsc4785ce2006-05-02 22:10:46 +0000253 // We need to unregister manually
254 m_registration.unregister();
255
Karl Pauls91761932006-04-28 11:12:54 +0000256 m_admin.stop();
257
Karl Paulsc8f79602006-05-01 23:01:13 +0000258 // This tasks will be unblocked once the queues are empty
259 final BlockTask asyncShutdownBlock = new BlockTask();
Karl Pauls91761932006-04-28 11:12:54 +0000260
Karl Paulsc8f79602006-05-01 23:01:13 +0000261 final BlockTask syncShutdownBlock = new BlockTask();
262
263 // Now close the queues. Note that already added tasks will be delivered
264 // The given shutdownTask will be executed once the queue is empty
265 m_asyncQueue.close(asyncShutdownBlock);
266
267 m_syncQueue.close(syncShutdownBlock);
Karl Pauls91761932006-04-28 11:12:54 +0000268
269 m_admin = null;
270
Karl Pauls91761932006-04-28 11:12:54 +0000271 m_asyncQueue = null;
272
273 m_syncQueue = null;
Karl Paulsc8f79602006-05-01 23:01:13 +0000274
Karl Paulsc4785ce2006-05-02 22:10:46 +0000275 m_registration = null;
276
Karl Pauls13f116c2006-05-14 15:12:25 +0000277 final DispatchTask task = m_pool.getTask(Thread.currentThread(), null);
278
279 if(null != task)
280 {
281 task.handover();
282 }
283
Karl Paulsc8f79602006-05-01 23:01:13 +0000284 asyncShutdownBlock.block();
285
286 syncShutdownBlock.block();
Karl Pauls13f116c2006-05-14 15:12:25 +0000287
288 m_pool.close();
289
290 m_pool = null;
Karl Pauls91761932006-04-28 11:12:54 +0000291 }
292
293 /*
294 * Create an AsyncDeliverTasks object that is used to dispatch asynchronous
295 * events. Additionally, the asynchronous dispatch queue is initialized and
296 * activated (i.e., a thread is started via the given ThreadPool).
297 */
298 private DeliverTasks createAsyncExecuters(final TaskHandler handler,
299 final TaskHandler handoverHandler, final Scheduler scheduler,
300 final ThreadPool pool)
301 {
302 // init the queue
303 final AsyncDeliverTasks result = new AsyncDeliverTasks(handler,
304 handoverHandler, pool);
305
306 // set-up the queue for asynchronous event delivery and activate it
307 // (i.e., a thread is started via the pool)
308 result.execute(new DispatchTask(handler, scheduler, result));
309
310 return result;
311 }
312
313 /*
314 * Create a SyncDeliverTasks object that is used to dispatch synchronous events.
315 * Additionally, the synchronous dispatch queue is initialized and activated
316 * (i.e., a thread is started via the given ThreadPool).
317 */
318 private DeliverTasks createSyncExecuters(final TaskHandler handler,
319 final Scheduler scheduler, final ThreadPool pool)
320 {
321 // init the queue
322 final SyncDeliverTasks result = new SyncDeliverTasks(handler, pool);
323
324 // set-up the queue for synchronous event delivery and activate it
325 // (i.e. a thread is started via the pool)
326 result.execute(new DispatchTask(handler, scheduler, result));
327
328 return result;
329 }
330
331 /*
332 * Returns either a new DelayScheduler with a delay of timeout or the
333 * Scheduler.NULL_SCHEDULER in case timeout is < 100 in which case timeout and
334 * subsequently black-listing is disabled.
335 */
336 private Scheduler createScheduler(final int timeout)
337 {
338 if(100 > timeout)
339 {
340 return Scheduler.NULL_SCHEDULER;
341 }
342
343 return new DelayScheduler(timeout);
344 }
345
346 /*
347 * Init the adapters in org.apache.felix.eventadmin.impl.adapter
348 */
349 private void adaptEvents(final BundleContext context, final EventAdmin admin)
350 {
351 new FrameworkEventAdapter(context, admin);
352
353 new BundleEventAdapter(context, admin);
354
355 new ServiceEventAdapter(context, admin);
356
357 new LogEventAdapter(context, admin);
358 }
359
360 /*
361 * Returns either the parsed int from the value of the property if it is set and
362 * not less then the min value or the default. Additionally, a warning is
363 * generated in case the value is erroneous (i.e., can not be parsed as an int or
364 * is less then the min value).
365 */
366 private int getIntProperty(final String key, final BundleContext context,
367 final int defaultValue, final int min)
368 {
369 final String value = context.getProperty(key);
370
371 if(null != value)
372 {
373 try {
374 final int result = Integer.parseInt(value);
375
376 if(result >= min)
377 {
378 return result;
379 }
380
381 LogWrapper.getLogger().log(LogWrapper.LOG_WARNING,
382 "Value for property: " + key + " is to low - Using default");
383 } catch (NumberFormatException e) {
384 LogWrapper.getLogger().log(LogWrapper.LOG_WARNING,
385 "Unable to parse property: " + key + " - Using default", e);
386 }
387 }
388
389 return defaultValue;
390 }
391
392
393 /*
394 * Returns true if the value of the property is set and is either 1, true, or yes
395 * Returns false if the value of the property is set and is either 0, false, or no
396 * Returns the defaultValue otherwise
397 */
398 private boolean getBooleanProperty(final String key, final BundleContext context,
399 final boolean defaultValue)
400 {
401 String value = context.getProperty(key);
402
403 if(null != value)
404 {
405 value = value.trim().toLowerCase();
406
407 if(0 < value.length() && ("0".equals(value) || "false".equals(value)
408 || "no".equals(value)))
409 {
410 return false;
411 }
412
413 if(0 < value.length() && ("1".equals(value) || "true".equals(value)
414 || "yes".equals(value)))
415 {
416 return true;
417 }
418 }
419
420 return defaultValue;
421 }
422}