| /******************************************************************************* |
| * Copyright (c) 2007, 2018 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.equinox.internal.event; |
| |
| import java.security.Permission; |
| import java.util.*; |
| import org.eclipse.osgi.framework.eventmgr.EventDispatcher; |
| 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; |
| |
| public class EventHandlerTracker extends ServiceTracker<EventHandler, EventHandlerWrapper> implements EventDispatcher<EventHandlerWrapper, Permission, Event> { |
| |
| private final LogTracker log; |
| //* List<EventHandlerWrapper> of all handlers with topic of "*" |
| private final List<EventHandlerWrapper> globalWildcard; |
| // Map<String,List<EventHandlerWrapper>> key is topic prefix of partial wildcard |
| private final Map<String, List<EventHandlerWrapper>> partialWildcard; |
| // Map<String,List<EventHandlerWrapper>> key is topic name |
| private final Map<String, List<EventHandlerWrapper>> topicName; |
| |
| public EventHandlerTracker(BundleContext context, LogTracker log) { |
| super(context, EventHandler.class.getName(), null); |
| this.log = log; |
| globalWildcard = new ArrayList<>(); |
| partialWildcard = new HashMap<>(); |
| topicName = new HashMap<>(); |
| } |
| |
| @Override |
| public EventHandlerWrapper addingService(ServiceReference<EventHandler> reference) { |
| EventHandlerWrapper wrapper = new EventHandlerWrapper(reference, context, log); |
| synchronized (this) { |
| if (wrapper.init()) { |
| bucket(wrapper); |
| } |
| } |
| return wrapper; |
| } |
| |
| @Override |
| public void modifiedService(ServiceReference<EventHandler> reference, EventHandlerWrapper service) { |
| synchronized (this) { |
| unbucket(service); |
| if (service.init()) { |
| bucket(service); |
| return; |
| } |
| } |
| |
| service.flush(); // needs to be called outside sync region |
| } |
| |
| @Override |
| public void removedService(ServiceReference<EventHandler> reference, EventHandlerWrapper service) { |
| synchronized (this) { |
| unbucket(service); |
| } |
| service.flush(); // needs to be called outside sync region |
| } |
| |
| /** |
| * Place the wrapper into the appropriate buckets. |
| * This is a performance optimization for event delivery. |
| * |
| * @param wrapper The wrapper to place in buckets. |
| * @GuardedBy this |
| */ |
| private void bucket(EventHandlerWrapper wrapper) { |
| final String[] topics = wrapper.getTopics(); |
| final int length = (topics == null) ? 0 : topics.length; |
| for (int i = 0; i < length; i++) { |
| String topic = topics[i]; |
| // global wildcard |
| if (topic.equals("*")) { //$NON-NLS-1$ |
| globalWildcard.add(wrapper); |
| } |
| // partial wildcard |
| else if (topic.endsWith("/*")) { //$NON-NLS-1$ |
| String key = topic.substring(0, topic.length() - 2); // Strip off "/*" from the end |
| List<EventHandlerWrapper> wrappers = partialWildcard.get(key); |
| if (wrappers == null) { |
| wrappers = new ArrayList<>(); |
| partialWildcard.put(key, wrappers); |
| } |
| wrappers.add(wrapper); |
| } |
| // simple topic name |
| else { |
| List<EventHandlerWrapper> wrappers = topicName.get(topic); |
| if (wrappers == null) { |
| wrappers = new ArrayList<>(); |
| topicName.put(topic, wrappers); |
| } |
| wrappers.add(wrapper); |
| } |
| } |
| } |
| |
| /** |
| * Remove the wrapper from the buckets. |
| * |
| * @param wrapper The wrapper to remove from the buckets. |
| * @GuardedBy this |
| */ |
| private void unbucket(EventHandlerWrapper wrapper) { |
| final String[] topics = wrapper.getTopics(); |
| final int length = (topics == null) ? 0 : topics.length; |
| for (int i = 0; i < length; i++) { |
| String topic = topics[i]; |
| // global wilcard |
| if (topic.equals("*")) { //$NON-NLS-1$ |
| globalWildcard.remove(wrapper); |
| } |
| // partial wildcard |
| else if (topic.endsWith("/*")) { //$NON-NLS-1$ |
| String key = topic.substring(0, topic.length() - 2); // Strip off "/*" from the end |
| List<EventHandlerWrapper> wrappers = partialWildcard.get(key); |
| if (wrappers != null) { |
| wrappers.remove(wrapper); |
| if (wrappers.isEmpty()) { |
| partialWildcard.remove(key); |
| } |
| } |
| } |
| // simple topic name |
| else { |
| List<EventHandlerWrapper> wrappers = topicName.get(topic); |
| if (wrappers != null) { |
| wrappers.remove(wrapper); |
| if (wrappers.isEmpty()) { |
| topicName.remove(topic); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Return the set of handlers which subscribe to the event topic. |
| * A set is used to ensure a handler is not called for an event more than once. |
| * |
| * @param topic |
| * @return a set of handlers |
| */ |
| public synchronized Set<EventHandlerWrapper> getHandlers(final String topic) { |
| // Use a set to remove duplicates |
| Set<EventHandlerWrapper> handlers = new LinkedHashSet<>(); |
| |
| // Add the "*" handlers |
| handlers.addAll(globalWildcard); |
| |
| // Add the handlers with partial matches |
| if (partialWildcard.size() > 0) { |
| int index = topic.lastIndexOf('/'); |
| while (index >= 0) { |
| String subTopic = topic.substring(0, index); |
| List<EventHandlerWrapper> wrappers = partialWildcard.get(subTopic); |
| if (wrappers != null) { |
| handlers.addAll(wrappers); |
| } |
| // Strip the last level from the topic. For example, org/osgi/framework becomes org/osgi. |
| // Wildcard topics are inserted into the map with the "/*" stripped off. |
| index = subTopic.lastIndexOf('/'); |
| } |
| } |
| |
| // Add the handlers for matching topic names |
| List<EventHandlerWrapper> wrappers = topicName.get(topic); |
| if (wrappers != null) { |
| handlers.addAll(wrappers); |
| } |
| |
| return handlers; |
| } |
| |
| /** |
| * Dispatches Event to EventHandlers |
| * |
| * @param eventListener |
| * @param listenerObject |
| * @param eventAction |
| * @param eventObject |
| * @see org.eclipse.osgi.framework.eventmgr.EventDispatcher#dispatchEvent(java.lang.Object, |
| * java.lang.Object, int, java.lang.Object) |
| */ |
| @Override |
| public void dispatchEvent(EventHandlerWrapper eventListener, Permission listenerObject, int eventAction, Event eventObject) { |
| eventListener.handleEvent(eventObject, listenerObject); |
| } |
| } |