/** * 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.useradmin.impl; import java.util.Properties; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import org.osgi.framework.Constants; import org.osgi.framework.ServiceReference; import org.osgi.service.event.Event; import org.osgi.service.event.EventAdmin; import org.osgi.service.event.EventConstants; import org.osgi.service.useradmin.Role; import org.osgi.service.useradmin.UserAdminEvent; import org.osgi.service.useradmin.UserAdminListener; /** * Provides an event dispatcher for delivering {@link UserAdminEvent}s asynchronously. */ public final class EventDispatcher implements Runnable { private static final String TOPIC_BASE = "org/osgi/service/useradmin/UserAdmin/"; private final EventAdmin m_eventAdmin; private final UserAdminListenerList m_listenerList; private final BlockingQueue m_eventQueue; private final Thread m_backgroundThread; /** * Creates a new {@link EventDispatcher} instance, and starts a background thread to deliver all events. * * @param eventAdmin the event admin to use, cannot be <code>null</code>; * @param listenerList the list with {@link UserAdminListener}s, cannot be <code>null</code>. * @throws IllegalArgumentException in case one of the given parameters was <code>null</code>. */ public EventDispatcher(EventAdmin eventAdmin, UserAdminListenerList listenerList) { if (eventAdmin == null) { throw new IllegalArgumentException("EventAdmin cannot be null!"); } if (listenerList == null) { throw new IllegalArgumentException("ListenerList cannot be null!"); } m_eventAdmin = eventAdmin; m_listenerList = listenerList; m_eventQueue = new LinkedBlockingQueue(); m_backgroundThread = new Thread(this, "UserAdmin event dispatcher"); } /** * Dispatches a given event for asynchronous delivery to all interested listeners, * including those using the {@link EventAdmin} service. * <p> * This method will perform a best-effort to dispatch the event to all listeners, i.e., * there is no guarantee that the listeners will actually obtain the event, nor any * notification is given in case delivery fails. * </p> * * @param event the event to dispatch, cannot be <code>null</code>. * @throws IllegalStateException in case this dispatcher is already stopped. */ public void dispatch(UserAdminEvent event) { if (!isRunning()) { return; } try { m_eventQueue.put(event); } catch (InterruptedException e) { // Restore interrupt flag... Thread.currentThread().interrupt(); } } /** * Starts this event dispatcher, allowing it to pick up events and deliver them. */ public void start() { if (!isRunning()) { m_backgroundThread.start(); } } /** * Signals this event dispatcher to stop its work and clean up all running threads. */ public void stop() { if (!isRunning()) { return; } // Add poison object to queue to let the background thread terminate... m_eventQueue.add(EventDispatcher.this); try { m_backgroundThread.join(); } catch (InterruptedException e) { // We're already stopping; so don't bother... } } /** * Returns whether or not the background thread is running. * * @return <code>true</code> if the background thread is running (alive), <code>false</code> otherwise. */ final boolean isRunning() { return m_backgroundThread.isAlive(); } /** * Provides the main event loop, which waits until an event is enqueued in order * to deliver it to any interested listener. */ public void run() { try { while (true) { // Blocks until a event is dispatched... Object event = m_eventQueue.take(); if (event instanceof UserAdminEvent) { // Got a "normal" user admin event; lets dispatch it further... deliverEventSynchronously((UserAdminEvent) event); } else { // Got a "poison" object; this means we must stop running... return; } } } catch (InterruptedException e) { // Restore interrupt flag, and terminate thread... Thread.currentThread().interrupt(); } } /** * Converts a given {@link UserAdminEvent} to a {@link Event} that can be * dispatched through the {@link EventAdmin} service. * * @param event * the event to convert, cannot be <code>null</code>. * @return a new {@link Event} instance containing the same set of * information as the given event, never <code>null</code>. */ private Event convertEvent(UserAdminEvent event) { String topic = getTopicName(event.getType()); Role role = event.getRole(); ServiceReference serviceRef = event.getServiceReference(); Properties props = new Properties(); props.put(EventConstants.EVENT_TOPIC, TOPIC_BASE.concat(topic)); props.put(EventConstants.EVENT, event); props.put("role", role); props.put("role.name", role.getName()); props.put("role.type", new Integer(role.getType())); if (serviceRef != null) { props.put(EventConstants.SERVICE, serviceRef); Object property; property = serviceRef.getProperty(Constants.SERVICE_ID); if (property != null) { props.put(EventConstants.SERVICE_ID, property); } property = serviceRef.getProperty(Constants.OBJECTCLASS); if (property != null) { props.put(EventConstants.SERVICE_OBJECTCLASS, property); } property = serviceRef.getProperty(Constants.SERVICE_PID); if (property != null) { props.put(EventConstants.SERVICE_PID, property); } } return new Event(topic, props); } /** * Delivers the given event synchronously to all interested listeners. * * @param event the event to deliver, cannot be <code>null</code>. */ private void deliverEventSynchronously(UserAdminEvent event) { // Asynchronously deliver an event to the EventAdmin service... m_eventAdmin.postEvent(convertEvent(event)); // Synchronously call all UserAdminListeners to deliver the event... UserAdminListener[] listeners = m_listenerList.getListeners(); for (int i = 0; i < listeners.length; i++) { listeners[i].roleChanged(event); } } /** * Converts a topic name for the given event-type. * * @param type the type of event to get the topic name for. * @return a topic name, never <code>null</code>. */ private String getTopicName(int type) { switch (type) { case UserAdminEvent.ROLE_CREATED: return "ROLE_CREATED"; case UserAdminEvent.ROLE_CHANGED: return "ROLE_CHANGED"; case UserAdminEvent.ROLE_REMOVED: return "ROLE_REMOVED"; default: return null; } } }