blob: d769bee49127383a5140a87b2c25a324bb5b103e [file] [log] [blame]
/**
* 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;
}
}
}